diff --git a/Userland/Libraries/LibTLS/Record.cpp b/Userland/Libraries/LibTLS/Record.cpp index 94596654b4..23fd488601 100644 --- a/Userland/Libraries/LibTLS/Record.cpp +++ b/Userland/Libraries/LibTLS/Record.cpp @@ -498,7 +498,7 @@ ssize_t TLSv12::handle_message(ReadonlyBytes buffer) } else { dbgln_if(TLS_DEBUG, "application data message of size {}", plain.size()); - if (m_context.application_buffer.try_append(plain.data(), plain.size()).is_error()) { + if (m_context.application_buffer.try_append(plain).is_error()) { payload_res = (i8)Error::DecryptionFailed; auto packet = build_alert(true, (u8)AlertDescription::DECRYPTION_FAILED_RESERVED); write_packet(packet); diff --git a/Userland/Libraries/LibTLS/Socket.cpp b/Userland/Libraries/LibTLS/Socket.cpp index c1a964a78a..88102acffb 100644 --- a/Userland/Libraries/LibTLS/Socket.cpp +++ b/Userland/Libraries/LibTLS/Socket.cpp @@ -27,8 +27,7 @@ ErrorOr TLSv12::read_some(Bytes bytes) return Bytes {}; } - TRY(m_context.application_buffer.slice(0, size_to_read)).span().copy_to(bytes); - m_context.application_buffer = TRY(m_context.application_buffer.slice(size_to_read, m_context.application_buffer.size() - size_to_read)); + m_context.application_buffer.transfer(bytes, size_to_read); return Bytes { bytes.data(), size_to_read }; } diff --git a/Userland/Libraries/LibTLS/TLSv12.h b/Userland/Libraries/LibTLS/TLSv12.h index b9ced4ba3b..527694f480 100644 --- a/Userland/Libraries/LibTLS/TLSv12.h +++ b/Userland/Libraries/LibTLS/TLSv12.h @@ -8,6 +8,7 @@ #include "Certificate.h" #include +#include #include #include #include @@ -197,6 +198,44 @@ struct Options { #undef OPTION_WITH_DEFAULTS }; +class SegmentedBuffer { +public: + [[nodiscard]] size_t size() const { return m_size; } + [[nodiscard]] bool is_empty() const { return m_size == 0; } + void transfer(Bytes dest, size_t size) + { + VERIFY(size <= dest.size()); + size_t transferred = 0; + while (transferred < size) { + auto& buffer = m_buffers.head(); + size_t to_transfer = min(buffer.size() - m_offset_into_current_buffer, size - transferred); + memcpy(dest.offset(transferred), buffer.data() + m_offset_into_current_buffer, to_transfer); + transferred += to_transfer; + m_offset_into_current_buffer += to_transfer; + if (m_offset_into_current_buffer >= buffer.size()) { + m_buffers.dequeue(); + m_offset_into_current_buffer = 0; + } + m_size -= to_transfer; + } + } + + AK::ErrorOr try_append(ReadonlyBytes data) + { + if (Checked::addition_would_overflow(m_size, data.size())) + return AK::Error::from_errno(EOVERFLOW); + + m_size += data.size(); + m_buffers.enqueue(TRY(ByteBuffer::copy(data))); + return {}; + } + +private: + size_t m_size { 0 }; + Queue m_buffers; + size_t m_offset_into_current_buffer { 0 }; +}; + struct Context { bool verify_chain(StringView host) const; bool verify_certificate_pair(Certificate const& subject, Certificate const& issuer) const; @@ -237,7 +276,7 @@ struct Context { ByteBuffer tls_buffer; - ByteBuffer application_buffer; + SegmentedBuffer application_buffer; bool is_child { false };