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
Notes: sideshowbarker 2024-07-19 01:17:44 +09:00
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/Encoder.h>
#include <LibIPC/Endpoint.h>
#include <LibIPC/File.h>
#include <LibIPC/Message.h>
)~~~");
@ -318,9 +319,9 @@ public:
static i32 static_message_id() { return (int)MessageID::@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) {
@ -436,7 +437,7 @@ public:
static String static_name() { 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 };
i32 message_endpoint_magic = 0;
@ -488,7 +489,7 @@ public:
message_generator.append(R"~~~(
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;
)~~~");
};

View file

@ -77,12 +77,25 @@ public:
auto buffer = message.encode();
// Prepend the message size.
uint32_t message_size = buffer.size();
buffer.prepend(reinterpret_cast<const u8*>(&message_size), sizeof(message_size));
uint32_t message_size = buffer.data.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;
while (total_nwritten < buffer.size()) {
auto nwritten = write(m_socket->fd(), buffer.data() + total_nwritten, buffer.size() - total_nwritten);
while (total_nwritten < buffer.data.size()) {
auto nwritten = write(m_socket->fd(), buffer.data.data() + total_nwritten, buffer.data.size() - total_nwritten);
if (nwritten < 0) {
switch (errno) {
case EPIPE:
@ -202,9 +215,9 @@ protected:
break;
index += sizeof(message_size);
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());
} 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());
} else {
dbgln("Failed to parse a message");

View file

@ -28,6 +28,9 @@
#include <AK/URL.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Dictionary.h>
#include <LibIPC/File.h>
#include <string.h>
#include <sys/socket.h>
namespace IPC {
@ -163,4 +166,21 @@ bool Decoder::decode(Dictionary& dictionary)
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 {
public:
explicit Decoder(InputMemoryStream& stream)
Decoder(InputMemoryStream& stream, int sockfd)
: m_stream(stream)
, m_sockfd(sockfd)
{
}
@ -63,6 +64,7 @@ public:
bool decode(ByteBuffer&);
bool decode(URL&);
bool decode(Dictionary&);
bool decode(File&);
template<typename K, typename V>
bool decode(HashMap<K, V>& hashmap)
{
@ -124,6 +126,7 @@ public:
private:
InputMemoryStream& m_stream;
int m_sockfd { -1 };
};
}

View file

@ -29,6 +29,7 @@
#include <AK/URL.h>
#include <LibIPC/Dictionary.h>
#include <LibIPC/Encoder.h>
#include <LibIPC/File.h>
namespace IPC {
@ -39,77 +40,77 @@ Encoder& Encoder::operator<<(bool value)
Encoder& Encoder::operator<<(u8 value)
{
m_buffer.append(value);
m_buffer.data.append(value);
return *this;
}
Encoder& Encoder::operator<<(u16 value)
{
m_buffer.ensure_capacity(m_buffer.size() + 2);
m_buffer.unchecked_append((u8)value);
m_buffer.unchecked_append((u8)(value >> 8));
m_buffer.data.ensure_capacity(m_buffer.data.size() + 2);
m_buffer.data.unchecked_append((u8)value);
m_buffer.data.unchecked_append((u8)(value >> 8));
return *this;
}
Encoder& Encoder::operator<<(u32 value)
{
m_buffer.ensure_capacity(m_buffer.size() + 4);
m_buffer.unchecked_append((u8)value);
m_buffer.unchecked_append((u8)(value >> 8));
m_buffer.unchecked_append((u8)(value >> 16));
m_buffer.unchecked_append((u8)(value >> 24));
m_buffer.data.ensure_capacity(m_buffer.data.size() + 4);
m_buffer.data.unchecked_append((u8)value);
m_buffer.data.unchecked_append((u8)(value >> 8));
m_buffer.data.unchecked_append((u8)(value >> 16));
m_buffer.data.unchecked_append((u8)(value >> 24));
return *this;
}
Encoder& Encoder::operator<<(u64 value)
{
m_buffer.ensure_capacity(m_buffer.size() + 8);
m_buffer.unchecked_append((u8)value);
m_buffer.unchecked_append((u8)(value >> 8));
m_buffer.unchecked_append((u8)(value >> 16));
m_buffer.unchecked_append((u8)(value >> 24));
m_buffer.unchecked_append((u8)(value >> 32));
m_buffer.unchecked_append((u8)(value >> 40));
m_buffer.unchecked_append((u8)(value >> 48));
m_buffer.unchecked_append((u8)(value >> 56));
m_buffer.data.ensure_capacity(m_buffer.data.size() + 8);
m_buffer.data.unchecked_append((u8)value);
m_buffer.data.unchecked_append((u8)(value >> 8));
m_buffer.data.unchecked_append((u8)(value >> 16));
m_buffer.data.unchecked_append((u8)(value >> 24));
m_buffer.data.unchecked_append((u8)(value >> 32));
m_buffer.data.unchecked_append((u8)(value >> 40));
m_buffer.data.unchecked_append((u8)(value >> 48));
m_buffer.data.unchecked_append((u8)(value >> 56));
return *this;
}
Encoder& Encoder::operator<<(i8 value)
{
m_buffer.append((u8)value);
m_buffer.data.append((u8)value);
return *this;
}
Encoder& Encoder::operator<<(i16 value)
{
m_buffer.ensure_capacity(m_buffer.size() + 2);
m_buffer.unchecked_append((u8)value);
m_buffer.unchecked_append((u8)(value >> 8));
m_buffer.data.ensure_capacity(m_buffer.data.size() + 2);
m_buffer.data.unchecked_append((u8)value);
m_buffer.data.unchecked_append((u8)(value >> 8));
return *this;
}
Encoder& Encoder::operator<<(i32 value)
{
m_buffer.ensure_capacity(m_buffer.size() + 4);
m_buffer.unchecked_append((u8)value);
m_buffer.unchecked_append((u8)(value >> 8));
m_buffer.unchecked_append((u8)(value >> 16));
m_buffer.unchecked_append((u8)(value >> 24));
m_buffer.data.ensure_capacity(m_buffer.data.size() + 4);
m_buffer.data.unchecked_append((u8)value);
m_buffer.data.unchecked_append((u8)(value >> 8));
m_buffer.data.unchecked_append((u8)(value >> 16));
m_buffer.data.unchecked_append((u8)(value >> 24));
return *this;
}
Encoder& Encoder::operator<<(i64 value)
{
m_buffer.ensure_capacity(m_buffer.size() + 8);
m_buffer.unchecked_append((u8)value);
m_buffer.unchecked_append((u8)(value >> 8));
m_buffer.unchecked_append((u8)(value >> 16));
m_buffer.unchecked_append((u8)(value >> 24));
m_buffer.unchecked_append((u8)(value >> 32));
m_buffer.unchecked_append((u8)(value >> 40));
m_buffer.unchecked_append((u8)(value >> 48));
m_buffer.unchecked_append((u8)(value >> 56));
m_buffer.data.ensure_capacity(m_buffer.data.size() + 8);
m_buffer.data.unchecked_append((u8)value);
m_buffer.data.unchecked_append((u8)(value >> 8));
m_buffer.data.unchecked_append((u8)(value >> 16));
m_buffer.data.unchecked_append((u8)(value >> 24));
m_buffer.data.unchecked_append((u8)(value >> 32));
m_buffer.data.unchecked_append((u8)(value >> 40));
m_buffer.data.unchecked_append((u8)(value >> 48));
m_buffer.data.unchecked_append((u8)(value >> 56));
return *this;
}
@ -130,7 +131,7 @@ Encoder& Encoder::operator<<(const char* 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;
}
@ -145,7 +146,7 @@ Encoder& Encoder::operator<<(const String& value)
Encoder& Encoder::operator<<(const ByteBuffer& value)
{
*this << static_cast<i32>(value.size());
m_buffer.append(value.data(), value.size());
m_buffer.data.append(value.data(), value.size());
return *this;
}
@ -163,4 +164,10 @@ Encoder& Encoder::operator<<(const Dictionary& dictionary)
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 URL&);
Encoder& operator<<(const Dictionary&);
Encoder& operator<<(const File&);
template<typename K, typename V>
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 Encoder;
class Message;
class File;
}

View file

@ -30,7 +30,10 @@
namespace IPC {
typedef Vector<u8, 1024> MessageBuffer;
struct MessageBuffer {
Vector<u8, 1024> data;
Vector<int> fds;
};
class Message {
public: