mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 19:27:44 +00:00
LibIPC: Support sending file descriptors :^)
It is now possible to use the special IPC::File type in message arguments. In C++, the type is nothing more than a wrapper over a file descriptor. But when serializing/deserializing IPC::File arguments, LibIPC will use the sendfd/recvfd kernel APIs instead of sending the integer inline. This makes it quite convenient to pass files over IPC, and will allow us to significantly tighten sandboxes in the future :^) Closes https://github.com/SerenityOS/serenity/issues/3643
This commit is contained in:
parent
fa2e3e2be4
commit
23dc3ff0c2
9 changed files with 148 additions and 50 deletions
|
@ -217,6 +217,7 @@ int main(int argc, char** argv)
|
||||||
#include <LibIPC/Dictionary.h>
|
#include <LibIPC/Dictionary.h>
|
||||||
#include <LibIPC/Encoder.h>
|
#include <LibIPC/Encoder.h>
|
||||||
#include <LibIPC/Endpoint.h>
|
#include <LibIPC/Endpoint.h>
|
||||||
|
#include <LibIPC/File.h>
|
||||||
#include <LibIPC/Message.h>
|
#include <LibIPC/Message.h>
|
||||||
)~~~");
|
)~~~");
|
||||||
|
|
||||||
|
@ -318,9 +319,9 @@ public:
|
||||||
static i32 static_message_id() { return (int)MessageID::@message.name@; }
|
static i32 static_message_id() { return (int)MessageID::@message.name@; }
|
||||||
virtual const char* message_name() const override { return "@endpoint.name@::@message.name@"; }
|
virtual const char* message_name() const override { return "@endpoint.name@::@message.name@"; }
|
||||||
|
|
||||||
static OwnPtr<@message.name@> decode(InputMemoryStream& stream)
|
static OwnPtr<@message.name@> decode(InputMemoryStream& stream, int sockfd)
|
||||||
{
|
{
|
||||||
IPC::Decoder decoder { stream };
|
IPC::Decoder decoder { stream, sockfd };
|
||||||
)~~~");
|
)~~~");
|
||||||
|
|
||||||
for (auto& parameter : parameters) {
|
for (auto& parameter : parameters) {
|
||||||
|
@ -436,7 +437,7 @@ public:
|
||||||
static String static_name() { return "@endpoint.name@"; }
|
static String static_name() { return "@endpoint.name@"; }
|
||||||
virtual String name() const override { return "@endpoint.name@"; }
|
virtual String name() const override { return "@endpoint.name@"; }
|
||||||
|
|
||||||
static OwnPtr<IPC::Message> decode_message(const ByteBuffer& buffer)
|
static OwnPtr<IPC::Message> decode_message(const ByteBuffer& buffer, int sockfd)
|
||||||
{
|
{
|
||||||
InputMemoryStream stream { buffer };
|
InputMemoryStream stream { buffer };
|
||||||
i32 message_endpoint_magic = 0;
|
i32 message_endpoint_magic = 0;
|
||||||
|
@ -488,7 +489,7 @@ public:
|
||||||
|
|
||||||
message_generator.append(R"~~~(
|
message_generator.append(R"~~~(
|
||||||
case (int)Messages::@endpoint.name@::MessageID::@message.name@:
|
case (int)Messages::@endpoint.name@::MessageID::@message.name@:
|
||||||
message = Messages::@endpoint.name@::@message.name@::decode(stream);
|
message = Messages::@endpoint.name@::@message.name@::decode(stream, sockfd);
|
||||||
break;
|
break;
|
||||||
)~~~");
|
)~~~");
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,12 +77,25 @@ public:
|
||||||
|
|
||||||
auto buffer = message.encode();
|
auto buffer = message.encode();
|
||||||
// Prepend the message size.
|
// Prepend the message size.
|
||||||
uint32_t message_size = buffer.size();
|
uint32_t message_size = buffer.data.size();
|
||||||
buffer.prepend(reinterpret_cast<const u8*>(&message_size), sizeof(message_size));
|
buffer.data.prepend(reinterpret_cast<const u8*>(&message_size), sizeof(message_size));
|
||||||
|
|
||||||
|
#ifdef __serenity__
|
||||||
|
for (int fd : buffer.fds) {
|
||||||
|
auto rc = sendfd(m_socket->fd(), fd);
|
||||||
|
if (rc < 0) {
|
||||||
|
perror("sendfd");
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!buffer.fds.is_empty())
|
||||||
|
warnln("fd passing is not supported on this platform, sorry :(");
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t total_nwritten = 0;
|
size_t total_nwritten = 0;
|
||||||
while (total_nwritten < buffer.size()) {
|
while (total_nwritten < buffer.data.size()) {
|
||||||
auto nwritten = write(m_socket->fd(), buffer.data() + total_nwritten, buffer.size() - total_nwritten);
|
auto nwritten = write(m_socket->fd(), buffer.data.data() + total_nwritten, buffer.data.size() - total_nwritten);
|
||||||
if (nwritten < 0) {
|
if (nwritten < 0) {
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
case EPIPE:
|
case EPIPE:
|
||||||
|
@ -202,9 +215,9 @@ protected:
|
||||||
break;
|
break;
|
||||||
index += sizeof(message_size);
|
index += sizeof(message_size);
|
||||||
auto remaining_bytes = ByteBuffer::wrap(bytes.data() + index, bytes.size() - index);
|
auto remaining_bytes = ByteBuffer::wrap(bytes.data() + index, bytes.size() - index);
|
||||||
if (auto message = LocalEndpoint::decode_message(remaining_bytes)) {
|
if (auto message = LocalEndpoint::decode_message(remaining_bytes, m_socket->fd())) {
|
||||||
m_unprocessed_messages.append(message.release_nonnull());
|
m_unprocessed_messages.append(message.release_nonnull());
|
||||||
} else if (auto message = PeerEndpoint::decode_message(remaining_bytes)) {
|
} else if (auto message = PeerEndpoint::decode_message(remaining_bytes, m_socket->fd())) {
|
||||||
m_unprocessed_messages.append(message.release_nonnull());
|
m_unprocessed_messages.append(message.release_nonnull());
|
||||||
} else {
|
} else {
|
||||||
dbgln("Failed to parse a message");
|
dbgln("Failed to parse a message");
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
#include <LibIPC/Decoder.h>
|
#include <LibIPC/Decoder.h>
|
||||||
#include <LibIPC/Dictionary.h>
|
#include <LibIPC/Dictionary.h>
|
||||||
|
#include <LibIPC/File.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
namespace IPC {
|
namespace IPC {
|
||||||
|
|
||||||
|
@ -163,4 +166,21 @@ bool Decoder::decode(Dictionary& dictionary)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Decoder::decode(File& file)
|
||||||
|
{
|
||||||
|
#ifdef __serenity__
|
||||||
|
int fd = recvfd(m_sockfd);
|
||||||
|
if (fd < 0) {
|
||||||
|
dbgln("recvfd: {}", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
file = File(fd);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
(void)file;
|
||||||
|
warnln("fd passing is not supported on this platform, sorry :(");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,9 @@ inline bool decode(Decoder&, T&)
|
||||||
|
|
||||||
class Decoder {
|
class Decoder {
|
||||||
public:
|
public:
|
||||||
explicit Decoder(InputMemoryStream& stream)
|
Decoder(InputMemoryStream& stream, int sockfd)
|
||||||
: m_stream(stream)
|
: m_stream(stream)
|
||||||
|
, m_sockfd(sockfd)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ public:
|
||||||
bool decode(ByteBuffer&);
|
bool decode(ByteBuffer&);
|
||||||
bool decode(URL&);
|
bool decode(URL&);
|
||||||
bool decode(Dictionary&);
|
bool decode(Dictionary&);
|
||||||
|
bool decode(File&);
|
||||||
template<typename K, typename V>
|
template<typename K, typename V>
|
||||||
bool decode(HashMap<K, V>& hashmap)
|
bool decode(HashMap<K, V>& hashmap)
|
||||||
{
|
{
|
||||||
|
@ -124,6 +126,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InputMemoryStream& m_stream;
|
InputMemoryStream& m_stream;
|
||||||
|
int m_sockfd { -1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
#include <LibIPC/Dictionary.h>
|
#include <LibIPC/Dictionary.h>
|
||||||
#include <LibIPC/Encoder.h>
|
#include <LibIPC/Encoder.h>
|
||||||
|
#include <LibIPC/File.h>
|
||||||
|
|
||||||
namespace IPC {
|
namespace IPC {
|
||||||
|
|
||||||
|
@ -39,77 +40,77 @@ Encoder& Encoder::operator<<(bool value)
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(u8 value)
|
Encoder& Encoder::operator<<(u8 value)
|
||||||
{
|
{
|
||||||
m_buffer.append(value);
|
m_buffer.data.append(value);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(u16 value)
|
Encoder& Encoder::operator<<(u16 value)
|
||||||
{
|
{
|
||||||
m_buffer.ensure_capacity(m_buffer.size() + 2);
|
m_buffer.data.ensure_capacity(m_buffer.data.size() + 2);
|
||||||
m_buffer.unchecked_append((u8)value);
|
m_buffer.data.unchecked_append((u8)value);
|
||||||
m_buffer.unchecked_append((u8)(value >> 8));
|
m_buffer.data.unchecked_append((u8)(value >> 8));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(u32 value)
|
Encoder& Encoder::operator<<(u32 value)
|
||||||
{
|
{
|
||||||
m_buffer.ensure_capacity(m_buffer.size() + 4);
|
m_buffer.data.ensure_capacity(m_buffer.data.size() + 4);
|
||||||
m_buffer.unchecked_append((u8)value);
|
m_buffer.data.unchecked_append((u8)value);
|
||||||
m_buffer.unchecked_append((u8)(value >> 8));
|
m_buffer.data.unchecked_append((u8)(value >> 8));
|
||||||
m_buffer.unchecked_append((u8)(value >> 16));
|
m_buffer.data.unchecked_append((u8)(value >> 16));
|
||||||
m_buffer.unchecked_append((u8)(value >> 24));
|
m_buffer.data.unchecked_append((u8)(value >> 24));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(u64 value)
|
Encoder& Encoder::operator<<(u64 value)
|
||||||
{
|
{
|
||||||
m_buffer.ensure_capacity(m_buffer.size() + 8);
|
m_buffer.data.ensure_capacity(m_buffer.data.size() + 8);
|
||||||
m_buffer.unchecked_append((u8)value);
|
m_buffer.data.unchecked_append((u8)value);
|
||||||
m_buffer.unchecked_append((u8)(value >> 8));
|
m_buffer.data.unchecked_append((u8)(value >> 8));
|
||||||
m_buffer.unchecked_append((u8)(value >> 16));
|
m_buffer.data.unchecked_append((u8)(value >> 16));
|
||||||
m_buffer.unchecked_append((u8)(value >> 24));
|
m_buffer.data.unchecked_append((u8)(value >> 24));
|
||||||
m_buffer.unchecked_append((u8)(value >> 32));
|
m_buffer.data.unchecked_append((u8)(value >> 32));
|
||||||
m_buffer.unchecked_append((u8)(value >> 40));
|
m_buffer.data.unchecked_append((u8)(value >> 40));
|
||||||
m_buffer.unchecked_append((u8)(value >> 48));
|
m_buffer.data.unchecked_append((u8)(value >> 48));
|
||||||
m_buffer.unchecked_append((u8)(value >> 56));
|
m_buffer.data.unchecked_append((u8)(value >> 56));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(i8 value)
|
Encoder& Encoder::operator<<(i8 value)
|
||||||
{
|
{
|
||||||
m_buffer.append((u8)value);
|
m_buffer.data.append((u8)value);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(i16 value)
|
Encoder& Encoder::operator<<(i16 value)
|
||||||
{
|
{
|
||||||
m_buffer.ensure_capacity(m_buffer.size() + 2);
|
m_buffer.data.ensure_capacity(m_buffer.data.size() + 2);
|
||||||
m_buffer.unchecked_append((u8)value);
|
m_buffer.data.unchecked_append((u8)value);
|
||||||
m_buffer.unchecked_append((u8)(value >> 8));
|
m_buffer.data.unchecked_append((u8)(value >> 8));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(i32 value)
|
Encoder& Encoder::operator<<(i32 value)
|
||||||
{
|
{
|
||||||
m_buffer.ensure_capacity(m_buffer.size() + 4);
|
m_buffer.data.ensure_capacity(m_buffer.data.size() + 4);
|
||||||
m_buffer.unchecked_append((u8)value);
|
m_buffer.data.unchecked_append((u8)value);
|
||||||
m_buffer.unchecked_append((u8)(value >> 8));
|
m_buffer.data.unchecked_append((u8)(value >> 8));
|
||||||
m_buffer.unchecked_append((u8)(value >> 16));
|
m_buffer.data.unchecked_append((u8)(value >> 16));
|
||||||
m_buffer.unchecked_append((u8)(value >> 24));
|
m_buffer.data.unchecked_append((u8)(value >> 24));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(i64 value)
|
Encoder& Encoder::operator<<(i64 value)
|
||||||
{
|
{
|
||||||
m_buffer.ensure_capacity(m_buffer.size() + 8);
|
m_buffer.data.ensure_capacity(m_buffer.data.size() + 8);
|
||||||
m_buffer.unchecked_append((u8)value);
|
m_buffer.data.unchecked_append((u8)value);
|
||||||
m_buffer.unchecked_append((u8)(value >> 8));
|
m_buffer.data.unchecked_append((u8)(value >> 8));
|
||||||
m_buffer.unchecked_append((u8)(value >> 16));
|
m_buffer.data.unchecked_append((u8)(value >> 16));
|
||||||
m_buffer.unchecked_append((u8)(value >> 24));
|
m_buffer.data.unchecked_append((u8)(value >> 24));
|
||||||
m_buffer.unchecked_append((u8)(value >> 32));
|
m_buffer.data.unchecked_append((u8)(value >> 32));
|
||||||
m_buffer.unchecked_append((u8)(value >> 40));
|
m_buffer.data.unchecked_append((u8)(value >> 40));
|
||||||
m_buffer.unchecked_append((u8)(value >> 48));
|
m_buffer.data.unchecked_append((u8)(value >> 48));
|
||||||
m_buffer.unchecked_append((u8)(value >> 56));
|
m_buffer.data.unchecked_append((u8)(value >> 56));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +131,7 @@ Encoder& Encoder::operator<<(const char* value)
|
||||||
|
|
||||||
Encoder& Encoder::operator<<(const StringView& value)
|
Encoder& Encoder::operator<<(const StringView& value)
|
||||||
{
|
{
|
||||||
m_buffer.append((const u8*)value.characters_without_null_termination(), value.length());
|
m_buffer.data.append((const u8*)value.characters_without_null_termination(), value.length());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ Encoder& Encoder::operator<<(const String& value)
|
||||||
Encoder& Encoder::operator<<(const ByteBuffer& value)
|
Encoder& Encoder::operator<<(const ByteBuffer& value)
|
||||||
{
|
{
|
||||||
*this << static_cast<i32>(value.size());
|
*this << static_cast<i32>(value.size());
|
||||||
m_buffer.append(value.data(), value.size());
|
m_buffer.data.append(value.data(), value.size());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,4 +164,10 @@ Encoder& Encoder::operator<<(const Dictionary& dictionary)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Encoder& Encoder::operator<<(const File& file)
|
||||||
|
{
|
||||||
|
m_buffer.fds.append(file.fd());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ public:
|
||||||
Encoder& operator<<(const ByteBuffer&);
|
Encoder& operator<<(const ByteBuffer&);
|
||||||
Encoder& operator<<(const URL&);
|
Encoder& operator<<(const URL&);
|
||||||
Encoder& operator<<(const Dictionary&);
|
Encoder& operator<<(const Dictionary&);
|
||||||
|
Encoder& operator<<(const File&);
|
||||||
template<typename K, typename V>
|
template<typename K, typename V>
|
||||||
Encoder& operator<<(const HashMap<K, V>& hashmap)
|
Encoder& operator<<(const HashMap<K, V>& hashmap)
|
||||||
{
|
{
|
||||||
|
|
49
Libraries/LibIPC/File.h
Normal file
49
Libraries/LibIPC/File.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace IPC {
|
||||||
|
|
||||||
|
class File {
|
||||||
|
public:
|
||||||
|
// Must have a default constructor, because LibIPC
|
||||||
|
// default-constructs arguments prior to decoding them.
|
||||||
|
File() { }
|
||||||
|
|
||||||
|
// Intentionally not `explicit`.
|
||||||
|
File(int fd)
|
||||||
|
: m_fd(fd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd() const { return m_fd; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_fd { -1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -32,5 +32,6 @@ class Decoder;
|
||||||
class Dictionary;
|
class Dictionary;
|
||||||
class Encoder;
|
class Encoder;
|
||||||
class Message;
|
class Message;
|
||||||
|
class File;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,10 @@
|
||||||
|
|
||||||
namespace IPC {
|
namespace IPC {
|
||||||
|
|
||||||
typedef Vector<u8, 1024> MessageBuffer;
|
struct MessageBuffer {
|
||||||
|
Vector<u8, 1024> data;
|
||||||
|
Vector<int> fds;
|
||||||
|
};
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue