[haiku-commits] haiku: hrev53800 - in src: tests/system/kernel/util tests/system/network/posixnet add-ons/kernel/network/protocols/tcp

  • From: Stephan Aßmus <superstippi@xxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Sat, 1 Feb 2020 06:52:38 -0500 (EST)

hrev53800 adds 1 changeset to branch 'master'
old head: 4a2bab12b14d0210ac4f09002f003f33ead81edf
new head: 2112748284f2249e5b811947e2fbdc4b95ea95d6
overview: 
https://git.haiku-os.org/haiku/log/?qt=range&q=2112748284f2+%5E4a2bab12b14d

----------------------------------------------------------------------------

2112748284f2: tcp: Fix KDL when sockets are reused
  
  This fixes a KDL that is triggered by the following scenario.
  
  1. A socket is created, attempting to establish a session
     between (loopback, ephemeral port) => (remote address, remote
     port).
  2. That socket ends up in the closed state because the remote is not
     accepting connections.
  3. The socket is re-used to connect to a different (remote address,
     remote port).
  
  The problem is that fConnectionHash is a BOpenHashTable<TCPEndpoint>,
  and inserting endpoint multiple times can create a linked list
  cycle (TCPEndpoint is an intrusive linked list node). That means that,
  even though TCPEndpoint's destructor removes itself from
  fConnectionHash, there will still be a pointer to it left behind,
  which means that future accesses within that hash table bucket will
  result in a segfault.
  
  The added fConnectionHash.Remove(endpoint) here prevents the KDL, as
  it ensures that socket reuse doesn't result in a cycle.
  
  Fixes #13927, see that ticket for a detailed explanation of the
  problem.
  
  Also added some regression tests:
  * Added SocketTests::ClientSocketReuseTest to PosixNetTest, which
    reproduces this KDL.
  * BOpenHashTable: Added tests to cover RemoveUnchecked and removal
    of an object that isn't in the table.
  
  Change-Id: If4bcc1e0d94350a5ad9ba8e7ae6f1b783b3f6d34
  Reviewed-on: https://review.haiku-os.org/c/haiku/+/2173
  Reviewed-by: Stephan Aßmus <superstippi@xxxxxx>

                                  [ Kyle Ambroff-Kao <kyle@xxxxxxxxxxxxxx> ]

----------------------------------------------------------------------------

Revision:    hrev53800
Commit:      2112748284f2249e5b811947e2fbdc4b95ea95d6
URL:         https://git.haiku-os.org/haiku/commit/?id=2112748284f2
Author:      Kyle Ambroff-Kao <kyle@xxxxxxxxxxxxxx>
Date:        Wed Jan 29 00:16:08 2020 UTC
Committer:   Stephan Aßmus <superstippi@xxxxxx>
Commit-Date: Sat Feb  1 11:52:34 2020 UTC

Ticket:      https://dev.haiku-os.org/ticket/13927

----------------------------------------------------------------------------

7 files changed, 173 insertions(+), 1 deletion(-)
.../network/protocols/tcp/EndpointManager.cpp    | 17 +++++
.../system/kernel/util/BOpenHashTableTest.cpp    | 75 ++++++++++++++++++++
.../system/kernel/util/BOpenHashTableTest.h      |  4 ++
src/tests/system/network/posixnet/Jamfile        |  1 +
.../network/posixnet/PosixNetTestAddon.cpp       |  3 +-
.../system/network/posixnet/SocketTests.cpp      | 60 ++++++++++++++++
src/tests/system/network/posixnet/SocketTests.h  | 14 ++++

----------------------------------------------------------------------------

diff --git a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp 
b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp
index 1739b6323d..ea71173376 100644
--- a/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp
+++ b/src/add-ons/kernel/network/protocols/tcp/EndpointManager.cpp
@@ -272,6 +272,8 @@ EndpointManager::SetConnection(TCPEndpoint* endpoint, const 
sockaddr* _local,
                local.SetPort(port);
        }
 
+       // We want to create a connection for (local, peer), so check to make 
sure
+       // that this pair is not already in use by an existing connection.
        if (_LookupConnection(*local, peer) != NULL)
                return EADDRINUSE;
 
@@ -279,6 +281,21 @@ EndpointManager::SetConnection(TCPEndpoint* endpoint, 
const sockaddr* _local,
        endpoint->PeerAddress().SetTo(peer);
        T(Connect(endpoint));
 
+       // BOpenHashTable doesn't support inserting duplicate objects. Since
+       // BOpenHashTable is a chained hash table where the items are required 
to
+       // be intrusive linked list nodes, inserting the same object twice will
+       // create a cycle in the linked list, which is not handled currently.
+       //
+       // We need to makes sure to remove any existing copy of this endpoint
+       // object from the table in order to handle calling connect() on a 
closed
+       // socket to connect to a different remote (address, port) than it was
+       // originally used for.
+       //
+       // We use RemoveUnchecked here because we don't want the hash table to
+       // resize itself after this removal when we are planning to just add
+       // another.
+       fConnectionHash.RemoveUnchecked(endpoint);
+
        fConnectionHash.Insert(endpoint);
        return B_OK;
 }
diff --git a/src/tests/system/kernel/util/BOpenHashTableTest.cpp 
b/src/tests/system/kernel/util/BOpenHashTableTest.cpp
index 08922aab19..acc7623cb1 100644
--- a/src/tests/system/kernel/util/BOpenHashTableTest.cpp
+++ b/src/tests/system/kernel/util/BOpenHashTableTest.cpp
@@ -75,6 +75,12 @@ CppUnit::Test* BOpenHashTableTest::Suite()
        suite->addTest(new CppUnit::TestCaller<BOpenHashTableTest>(
                                           "BOpenHashTable::Insert test",
                                           &BOpenHashTableTest::InsertTest));
+       suite->addTest(new CppUnit::TestCaller<BOpenHashTableTest>(
+                                          "BOpenHashTable::Insert unchecked 
test",
+                                          
&BOpenHashTableTest::InsertUncheckedTest));
+       suite->addTest(new CppUnit::TestCaller<BOpenHashTableTest>(
+                                          "BOpenHashTable::Insert unchecked 
uninitialized test",
+                                          
&BOpenHashTableTest::InsertUncheckedUninitializedTest));
        suite->addTest(new CppUnit::TestCaller<BOpenHashTableTest>(
                                           "BOpenHashTable::Iterate and count 
test",
                                           
&BOpenHashTableTest::IterateAndCountTest));
@@ -87,6 +93,12 @@ CppUnit::Test* BOpenHashTableTest::Suite()
        suite->addTest(new CppUnit::TestCaller<BOpenHashTableTest>(
                                           "BOpenHashTable::Remove test",
                                           &BOpenHashTableTest::RemoveTest));
+       suite->addTest(new CppUnit::TestCaller<BOpenHashTableTest>(
+                                          "BOpenHashTable::Remove unchecked 
test",
+                                          
&BOpenHashTableTest::RemoveUncheckedTest));
+       suite->addTest(new CppUnit::TestCaller<BOpenHashTableTest>(
+                                          "BOpenHashTable::Remove when not 
present test",
+                                          
&BOpenHashTableTest::RemoveWhenNotPresentTest));
        suite->addTest(new CppUnit::TestCaller<BOpenHashTableTest>(
                                           "BOpenHashTable::Duplicate insert 
test",
                                           
&BOpenHashTableTest::DuplicateInsertTest));
@@ -124,6 +136,28 @@ void BOpenHashTableTest::InsertTest()
 }
 
 
+void BOpenHashTableTest::InsertUncheckedTest()
+{
+       Entry entry(123);
+
+       BOpenHashTable<EntryDefinition> table;
+       CPPUNIT_ASSERT_EQUAL(table.Init(), B_OK);
+
+       table.InsertUnchecked(&entry);
+}
+
+
+void BOpenHashTableTest::InsertUncheckedUninitializedTest()
+{
+       Entry entry(123);
+
+       BOpenHashTable<EntryDefinition> table;
+       CPPUNIT_ASSERT_EQUAL(table.Init(), B_OK);
+
+       table.InsertUnchecked(&entry);
+}
+
+
 void BOpenHashTableTest::IterateAndCountTest() {
        const size_t kEntryCount = 20;
 
@@ -144,6 +178,7 @@ void BOpenHashTableTest::IterateAndCountTest() {
        while (iterator.HasNext()) {
                Entry* entry = iterator.Next();
                CPPUNIT_ASSERT_EQUAL(0, map & (1 << entry->Value()));
+               map |= (1 << entry->Value());
        }
 
        CPPUNIT_ASSERT_EQUAL(map, (1 << kEntryCount) - 1);
@@ -209,6 +244,46 @@ void BOpenHashTableTest::RemoveTest() {
 }
 
 
+void BOpenHashTableTest::RemoveUncheckedTest()
+{
+       Entry entry(123);
+
+       BOpenHashTable<EntryDefinition> table;
+       CPPUNIT_ASSERT_EQUAL(table.Init(0), B_OK);
+
+       CPPUNIT_ASSERT_EQUAL(table.Insert(&entry), B_OK);
+       CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry);
+
+       table.RemoveUnchecked(&entry);
+       CPPUNIT_ASSERT_EQUAL(table.Lookup(123), NULL);
+}
+
+
+void BOpenHashTableTest::RemoveWhenNotPresentTest()
+{
+       Entry entry1(123);
+       Entry entry2(456);
+       Entry entry3(789);
+
+       BOpenHashTable<EntryDefinition> table;
+       CPPUNIT_ASSERT_EQUAL(table.Init(), B_OK);
+
+       // Only add the first two entries.
+       table.Insert(&entry1);
+       table.Insert(&entry2);
+
+       // entry3 is not in the table, but we'll remove it anyway.
+       table.Remove(&entry3);
+       table.RemoveUnchecked(&entry3);
+
+       // The original two entries should still be there.
+       CPPUNIT_ASSERT_EQUAL(table.CountElements(), 2);
+       CPPUNIT_ASSERT_EQUAL(table.Lookup(123), &entry1);
+       CPPUNIT_ASSERT_EQUAL(table.Lookup(456), &entry2);
+       CPPUNIT_ASSERT_EQUAL(table.Lookup(789), NULL);
+}
+
+
 void BOpenHashTableTest::DuplicateInsertTest()
 {
        Entry entry(123);
diff --git a/src/tests/system/kernel/util/BOpenHashTableTest.h 
b/src/tests/system/kernel/util/BOpenHashTableTest.h
index a5500dcdfa..c3bbe4d5ab 100644
--- a/src/tests/system/kernel/util/BOpenHashTableTest.h
+++ b/src/tests/system/kernel/util/BOpenHashTableTest.h
@@ -8,10 +8,14 @@ public:
        BOpenHashTableTest(std::string name = "");
 
        void InsertTest();
+       void InsertUncheckedTest();
+       void InsertUncheckedUninitializedTest();
        void IterateAndCountTest();
        void ResizeTest();
        void LookupTest();
        void RemoveTest();
+       void RemoveUncheckedTest();
+       void RemoveWhenNotPresentTest();
        void DuplicateInsertTest();
        void DisableAutoExpandTest();
        void InitWithZeroSizeTest();
diff --git a/src/tests/system/network/posixnet/Jamfile 
b/src/tests/system/network/posixnet/Jamfile
index 673cc11a0d..25628d4354 100644
--- a/src/tests/system/network/posixnet/Jamfile
+++ b/src/tests/system/network/posixnet/Jamfile
@@ -6,6 +6,7 @@ UnitTestLib posixnettest.so :
        PosixNetTestAddon.cpp
 
        GetAddrInfo.cpp
+       SocketTests.cpp
 
        : be $(TARGET_NETWORK_LIBS) [ TargetLibstdc++ ]
        ;
diff --git a/src/tests/system/network/posixnet/PosixNetTestAddon.cpp 
b/src/tests/system/network/posixnet/PosixNetTestAddon.cpp
index 8817c8af6c..2b0e918150 100644
--- a/src/tests/system/network/posixnet/PosixNetTestAddon.cpp
+++ b/src/tests/system/network/posixnet/PosixNetTestAddon.cpp
@@ -8,6 +8,7 @@
 #include <TestSuiteAddon.h>
 
 #include "GetAddrInfo.h"
+#include "SocketTests.h"
 
 
 BTestSuite*
@@ -16,7 +17,7 @@ getTestSuite()
        BTestSuite* suite = new BTestSuite("PosixNet");
 
        GetAddrInfoTest::AddTests(*suite);
+       SocketTests::AddTests(*suite);
 
        return suite;
 }
-
diff --git a/src/tests/system/network/posixnet/SocketTests.cpp 
b/src/tests/system/network/posixnet/SocketTests.cpp
new file mode 100644
index 0000000000..cdca9337b5
--- /dev/null
+++ b/src/tests/system/network/posixnet/SocketTests.cpp
@@ -0,0 +1,60 @@
+#include "SocketTests.h"
+
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestCaller.h>
+#include <cppunit/TestSuite.h>
+
+
+// This test reproduces a KDL from issue #13927 where a socket is created
+// and an attempt is made to connect to an address, which fails. The socket
+// is closed and then reused to connect to a *different* address.
+void SocketTests::ClientSocketReuseTest()
+{
+       // TODO: Try to find unused ports instead of using these hard-coded 
ones.
+       const uint16_t kFirstPort = 14025;
+       const uint16_t kSecondPort = 14026;
+
+       int fd = ::socket(AF_INET, SOCK_STREAM, 0);
+       CPPUNIT_ASSERT(fd > 0);
+
+       // Connect to 127.0.0.1:kFirstPort
+       sockaddr_in address;
+       memset(&address, 0, sizeof(sockaddr_in));
+       address.sin_family = AF_INET;
+       address.sin_port = htons(kFirstPort);
+       address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+       int connect_result = ::connect(
+               fd,
+               reinterpret_cast<const sockaddr *>(&address),
+               sizeof(struct sockaddr));
+       CPPUNIT_ASSERT_EQUAL(connect_result, -1);
+
+       // Connection to 127.0.0.1:kFirstPort failed as expected.
+       // Now try connecting to 127.0.0.1:kSecondPort.
+       address.sin_port = htons(kSecondPort);
+       connect_result = ::connect(
+               fd,
+               reinterpret_cast<const sockaddr *>(&address),
+               sizeof(struct sockaddr));
+       CPPUNIT_ASSERT_EQUAL(connect_result, -1);
+
+       close(fd);
+}
+
+
+void SocketTests::AddTests(BTestSuite &parent) {
+       CppUnit::TestSuite &suite = *new CppUnit::TestSuite("SocketTests");
+
+       suite.addTest(new CppUnit::TestCaller<SocketTests>(
+               "SocketTests::ClientSocketReuseTest",
+               &SocketTests::ClientSocketReuseTest));
+
+       parent.addTest("SocketTests", &suite);
+}
diff --git a/src/tests/system/network/posixnet/SocketTests.h 
b/src/tests/system/network/posixnet/SocketTests.h
new file mode 100644
index 0000000000..2dd3d835d4
--- /dev/null
+++ b/src/tests/system/network/posixnet/SocketTests.h
@@ -0,0 +1,14 @@
+#ifndef SOCKET_TESTS_H
+#define SOCKET_TESTS_H
+
+#include <TestCase.h>
+#include <TestSuite.h>
+
+class SocketTests : public BTestCase {
+ public:
+                       void    ClientSocketReuseTest();
+
+       static  void    AddTests(BTestSuite& suite);
+};
+
+#endif // SOCKET_TESTS_H


Other related posts:

  • » [haiku-commits] haiku: hrev53800 - in src: tests/system/kernel/util tests/system/network/posixnet add-ons/kernel/network/protocols/tcp - Stephan Aßmus