mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-04-18 09:50:27 +00:00
AnonymousBuffer is backed by shared memory, not heap allocation. The MAX_DECODED_SIZE limit in decode_size() is meant to prevent OOM from malicious peers claiming huge sizes that would cause heap allocations, but this doesn't apply to AnonymousBuffer since the memory is already allocated by the sender. This fixes decoding of large animated images (e.g. 300 frames at 240x240) where the total bitmap data exceeds 64 MiB.
204 lines
5.4 KiB
C++
204 lines
5.4 KiB
C++
/*
|
|
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
|
* Copyright (c) 2023-2025, Tim Flynn <trflynn89@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/IPv4Address.h>
|
|
#include <AK/IPv6Address.h>
|
|
#include <AK/JsonValue.h>
|
|
#include <AK/NumericLimits.h>
|
|
#include <AK/Types.h>
|
|
#include <AK/Utf16String.h>
|
|
#include <LibCore/AnonymousBuffer.h>
|
|
#include <LibCore/Proxy.h>
|
|
#include <LibCore/Socket.h>
|
|
#include <LibIPC/Decoder.h>
|
|
#include <LibIPC/File.h>
|
|
#include <LibURL/Parser.h>
|
|
#include <LibURL/URL.h>
|
|
|
|
namespace IPC {
|
|
|
|
// Maximum size for decoded containers (strings, buffers, vectors, etc.)
|
|
// This prevents a malicious peer from claiming huge sizes to cause OOM.
|
|
static constexpr size_t MAX_DECODED_SIZE = 64 * MiB;
|
|
|
|
ErrorOr<size_t> Decoder::decode_size()
|
|
{
|
|
auto size = static_cast<size_t>(TRY(decode<u32>()));
|
|
if (size > MAX_DECODED_SIZE)
|
|
return Error::from_string_literal("IPC decode: Size exceeds maximum allowed");
|
|
return size;
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<String> decode(Decoder& decoder)
|
|
{
|
|
auto length = TRY(decoder.decode_size());
|
|
return String::from_stream(decoder.stream(), length);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<Utf16String> decode(Decoder& decoder)
|
|
{
|
|
auto is_ascii = TRY(decoder.decode<bool>());
|
|
auto length_in_code_units = TRY(decoder.decode_size());
|
|
|
|
return Utf16String::from_ipc_stream(decoder.stream(), length_in_code_units, is_ascii);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<ByteString> decode(Decoder& decoder)
|
|
{
|
|
auto length = TRY(decoder.decode_size());
|
|
if (length == 0)
|
|
return ByteString::empty();
|
|
|
|
return ByteString::create_and_overwrite(length, [&](Bytes bytes) -> ErrorOr<void> {
|
|
TRY(decoder.decode_into(bytes));
|
|
return {};
|
|
});
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<ByteBuffer> decode(Decoder& decoder)
|
|
{
|
|
auto length = TRY(decoder.decode_size());
|
|
if (length == 0)
|
|
return ByteBuffer {};
|
|
|
|
auto buffer = TRY(ByteBuffer::create_uninitialized(length));
|
|
auto bytes = buffer.bytes();
|
|
|
|
TRY(decoder.decode_into(bytes));
|
|
return buffer;
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<JsonValue> decode(Decoder& decoder)
|
|
{
|
|
auto json = TRY(decoder.decode<ByteString>());
|
|
return JsonValue::from_string(json);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<AK::Duration> decode(Decoder& decoder)
|
|
{
|
|
auto nanoseconds = TRY(decoder.decode<i64>());
|
|
return AK::Duration::from_nanoseconds(nanoseconds);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<UnixDateTime> decode(Decoder& decoder)
|
|
{
|
|
auto nanoseconds = TRY(decoder.decode<i64>());
|
|
return AK::UnixDateTime::from_nanoseconds_since_epoch(nanoseconds);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<IPv4Address> decode(Decoder& decoder)
|
|
{
|
|
auto ipv4 = TRY(decoder.decode<u32>());
|
|
return IPv4Address(ipv4);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<IPv6Address> decode(Decoder& decoder)
|
|
{
|
|
auto ipv6 = TRY(decoder.decode<Array<u8, 16>>());
|
|
return IPv6Address(ipv6);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<URL::URL> decode(Decoder& decoder)
|
|
{
|
|
auto url_string = TRY(decoder.decode<ByteString>());
|
|
auto url = URL::Parser::basic_parse(url_string);
|
|
if (!url.has_value())
|
|
return Error::from_string_view("Failed to parse URL in IPC Decode"sv);
|
|
|
|
bool has_blob_url = TRY(decoder.decode<bool>());
|
|
if (!has_blob_url)
|
|
return url.release_value();
|
|
|
|
url->set_blob_url_entry(URL::BlobURLEntry {
|
|
.object = TRY(decoder.decode<URL::BlobURLEntry::Object>()),
|
|
.environment { .origin = TRY(decoder.decode<URL::Origin>()) },
|
|
});
|
|
|
|
return url.release_value();
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<URL::Origin> decode(Decoder& decoder)
|
|
{
|
|
auto is_opaque = TRY(decoder.decode<bool>());
|
|
if (is_opaque) {
|
|
auto nonce = TRY(decoder.decode<URL::Origin::Nonce>());
|
|
return URL::Origin { nonce };
|
|
}
|
|
|
|
auto scheme = TRY(decoder.decode<Optional<String>>());
|
|
auto host = TRY(decoder.decode<URL::Host>());
|
|
auto port = TRY(decoder.decode<Optional<u16>>());
|
|
auto domain = TRY(decoder.decode<Optional<String>>());
|
|
|
|
return URL::Origin { move(scheme), move(host), port, move(domain) };
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<URL::Host> decode(Decoder& decoder)
|
|
{
|
|
auto value = TRY(decoder.decode<URL::Host::VariantType>());
|
|
return URL::Host { move(value) };
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<Empty> decode(Decoder&)
|
|
{
|
|
return Empty {};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<Core::AnonymousBuffer> decode(Decoder& decoder)
|
|
{
|
|
if (auto valid = TRY(decoder.decode<bool>()); !valid)
|
|
return Core::AnonymousBuffer {};
|
|
|
|
// NOTE: We don't use decode_size() here since AnonymousBuffer is backed by
|
|
// shared memory, not heap allocation. The MAX_DECODED_SIZE limit doesn't
|
|
// apply because the memory is already allocated by the sender.
|
|
auto size = static_cast<size_t>(TRY(decoder.decode<u32>()));
|
|
auto anon_file = TRY(decoder.decode<IPC::File>());
|
|
|
|
return Core::AnonymousBuffer::create_from_anon_fd(anon_file.take_fd(), size);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<Core::ProxyData> decode(Decoder& decoder)
|
|
{
|
|
auto type = TRY(decoder.decode<Core::ProxyData::Type>());
|
|
auto host_ipv4 = IPv4Address(TRY(decoder.decode<u32>()));
|
|
auto port = TRY(decoder.decode<u16>());
|
|
|
|
return Core::ProxyData { type, host_ipv4, port };
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<URL::BlobURLEntry::Blob> decode<URL::BlobURLEntry::Blob>(Decoder& decoder)
|
|
{
|
|
return URL::BlobURLEntry::Blob {
|
|
.type = TRY(decoder.decode<String>()),
|
|
.data = TRY(decoder.decode<ByteBuffer>())
|
|
};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<URL::BlobURLEntry::MediaSource> decode<URL::BlobURLEntry::MediaSource>(Decoder&)
|
|
{
|
|
return URL::BlobURLEntry::MediaSource {};
|
|
}
|
|
|
|
}
|