diff options
author | Gunnar Beutner <gbeutner@serenityos.org> | 2022-11-01 10:04:13 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-11-01 14:31:48 +0000 |
commit | a9888d4ea05b04cfb7f56ac4793670bb524de996 (patch) | |
tree | e697c6badf4ce1552120778ed7f737e8267f1c28 | |
parent | ab8b04368476408c41d0a2cdf68e27358a4ca5f4 (diff) | |
download | serenity-a9888d4ea05b04cfb7f56ac4793670bb524de996.zip |
AK+Kernel: Handle some allocation failures in IPv4Socket and TCPSocket
This adds try_* methods to AK::SinglyLinkedList and
AK::SinglyLinkedListWithCount and updates the network stack to use
those to gracefully handle allocation failures.
Refs #6369.
-rw-r--r-- | AK/SinglyLinkedList.h | 67 | ||||
-rw-r--r-- | AK/SinglyLinkedListWithCount.h | 14 | ||||
-rw-r--r-- | Kernel/Net/IPv4Socket.cpp | 6 | ||||
-rw-r--r-- | Kernel/Net/TCPSocket.cpp | 25 |
4 files changed, 87 insertions, 25 deletions
diff --git a/AK/SinglyLinkedList.h b/AK/SinglyLinkedList.h index 79babf10e5..11a574495f 100644 --- a/AK/SinglyLinkedList.h +++ b/AK/SinglyLinkedList.h @@ -7,6 +7,7 @@ #pragma once #include <AK/Assertions.h> +#include <AK/Error.h> #include <AK/Find.h> #include <AK/StdLibExtras.h> #include <AK/Traits.h> @@ -148,31 +149,51 @@ public: } template<typename U = T> - void append(U&& value) + ErrorOr<void> try_append(U&& value) { - auto* node = new Node(forward<U>(value)); + auto* node = new (nothrow) Node(forward<U>(value)); + if (!node) + return Error::from_errno(ENOMEM); if (!m_head) { m_head = node; m_tail = node; - return; + return {}; } m_tail->next = node; m_tail = node; + return {}; } template<typename U = T> - void prepend(U&& value) + ErrorOr<void> try_prepend(U&& value) { - auto* node = new Node(forward<U>(value)); + auto* node = new (nothrow) Node(forward<U>(value)); + if (!node) + return Error::from_errno(ENOMEM); if (!m_head) { m_head = node; m_tail = node; - return; + return {}; } node->next = m_head; m_head = node; + return {}; + } + +#ifndef KERNEL + template<typename U = T> + void append(U&& value) + { + MUST(try_append(forward<U>(value))); } + template<typename U = T> + void prepend(U&& value) + { + MUST(try_prepend(forward<U>(value))); + } +#endif + bool contains_slow(const T& value) const { return find(value) != end(); @@ -211,32 +232,50 @@ public: } template<typename U = T> - void insert_before(Iterator iterator, U&& value) + ErrorOr<void> try_insert_before(Iterator iterator, U&& value) { - auto* node = new Node(forward<U>(value)); + auto* node = new (nothrow) Node(forward<U>(value)); + if (!node) + return Error::from_errno(ENOMEM); node->next = iterator.m_node; if (m_head == iterator.m_node) m_head = node; if (iterator.m_prev) iterator.m_prev->next = node; + return {}; } template<typename U = T> - void insert_after(Iterator iterator, U&& value) + ErrorOr<void> try_insert_after(Iterator iterator, U&& value) { - if (iterator.is_end()) { - append(value); - return; - } + if (iterator.is_end()) + return try_append(value); - auto* node = new Node(forward<U>(value)); + auto* node = new (nothrow) Node(forward<U>(value)); + if (!node) + return Error::from_errno(ENOMEM); node->next = iterator.m_node->next; iterator.m_node->next = node; if (m_tail == iterator.m_node) m_tail = node; + return {}; + } + +#ifndef KERNEL + template<typename U = T> + void insert_before(Iterator iterator, U&& value) + { + MUST(try_insert_before(iterator, forward<U>(value))); + } + + template<typename U = T> + void insert_after(Iterator iterator, U&& value) + { + MUST(try_insert_after(iterator, forward<U>(value))); } +#endif void remove(Iterator& iterator) { diff --git a/AK/SinglyLinkedListWithCount.h b/AK/SinglyLinkedListWithCount.h index 4f47ef7a24..39de628610 100644 --- a/AK/SinglyLinkedListWithCount.h +++ b/AK/SinglyLinkedListWithCount.h @@ -60,11 +60,21 @@ public: } template<typename U = T> + ErrorOr<void> try_append(U&& value) + { + auto result = List::try_append(forward<T>(value)); + if (!result.is_error()) + m_count++; + return result; + } + +#ifndef KERNEL + template<typename U = T> void append(U&& value) { - m_count++; - return List::append(forward<T>(value)); + MUST(try_append(forward<T>(value))); } +#endif bool contains_slow(const T& value) const { diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp index d18a08d795..5b66152ea7 100644 --- a/Kernel/Net/IPv4Socket.cpp +++ b/Kernel/Net/IPv4Socket.cpp @@ -449,7 +449,11 @@ bool IPv4Socket::did_receive(IPv4Address const& source_address, u16 source_port, dbgln("IPv4Socket: did_receive unable to allocate storage for incoming packet."); return false; } - m_receive_queue.append({ source_address, source_port, packet_timestamp, data_or_error.release_value() }); + auto result = m_receive_queue.try_append({ source_address, source_port, packet_timestamp, data_or_error.release_value() }); + if (result.is_error()) { + dbgln("IPv4Socket: Dropped incoming packet because appending to the receive queue failed."); + return false; + } set_can_read(true); } m_bytes_received += packet_size; diff --git a/Kernel/Net/TCPSocket.cpp b/Kernel/Net/TCPSocket.cpp index b19e450f49..2cd19f5137 100644 --- a/Kernel/Net/TCPSocket.cpp +++ b/Kernel/Net/TCPSocket.cpp @@ -274,20 +274,29 @@ ErrorOr<void> TCPSocket::send_tcp_packet(u16 flags, UserOrKernelBuffer const* pa tcp_packet.set_checksum(compute_tcp_checksum(local_address(), peer_address(), tcp_packet, payload_size)); - routing_decision.adapter->send_packet(packet->bytes()); - - m_packets_out++; - m_bytes_out += buffer_size; - if (tcp_packet.has_syn() || payload_size > 0) { + bool expect_ack { tcp_packet.has_syn() || payload_size > 0 }; + if (expect_ack) { + bool append_failed { false }; m_unacked_packets.with_exclusive([&](auto& unacked_packets) { - unacked_packets.packets.append({ m_sequence_number, move(packet), ipv4_payload_offset, *routing_decision.adapter }); + auto result = unacked_packets.packets.try_append({ m_sequence_number, packet, ipv4_payload_offset, *routing_decision.adapter }); + if (result.is_error()) { + dbgln("TCPSocket: Dropped outbound packet because try_append() failed"); + append_failed = true; + return; + } unacked_packets.size += payload_size; enqueue_for_retransmit(); }); - } else { - routing_decision.adapter->release_packet_buffer(*packet); + if (append_failed) + return set_so_error(ENOMEM); } + m_packets_out++; + m_bytes_out += buffer_size; + routing_decision.adapter->send_packet(packet->bytes()); + if (!expect_ack) + routing_decision.adapter->release_packet_buffer(*packet); + return {}; } |