Parcourir la source

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.
Gunnar Beutner il y a 2 ans
Parent
commit
a9888d4ea0
4 fichiers modifiés avec 87 ajouts et 25 suppressions
  1. 53 14
      AK/SinglyLinkedList.h
  2. 12 2
      AK/SinglyLinkedListWithCount.h
  3. 5 1
      Kernel/Net/IPv4Socket.cpp
  4. 17 8
      Kernel/Net/TCPSocket.cpp

+ 53 - 14
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)
     {

+ 12 - 2
AK/SinglyLinkedListWithCount.h

@@ -59,12 +59,22 @@ public:
         return List::take_first();
     }
 
+    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
     {

+ 5 - 1
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;

+ 17 - 8
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 {};
 }