1
Fork 0
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:
Sergey Bugaev 2020-11-21 21:59:12 +03:00 committed by Andreas Kling
parent fa2e3e2be4
commit 23dc3ff0c2
9 changed files with 148 additions and 50 deletions

View file

@ -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;
)~~~"); )~~~");
}; };

View file

@ -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");

View file

@ -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
}
} }

View file

@ -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 };
}; };
} }

View file

@ -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;
}
} }

View file

@ -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
View 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 };
};
}

View file

@ -32,5 +32,6 @@ class Decoder;
class Dictionary; class Dictionary;
class Encoder; class Encoder;
class Message; class Message;
class File;
} }

View 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: