From dd982e37fc4465f973111f0faef1e0ce3a7456c5 Mon Sep 17 00:00:00 2001 From: Evgenii Malygin <2518859+678098@users.noreply.github.com> Date: Fri, 18 Sep 2026 16:55:21 -0400 Subject: [PATCH 1/2] UT[ntcd_machine]: add `verifyReceiveIntoString` test function Added a test function to verify receiving data into a string with spare capacity. --- groups/ntc/ntcd/ntcd_machine.t.cpp | 128 +++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/groups/ntc/ntcd/ntcd_machine.t.cpp b/groups/ntc/ntcd/ntcd_machine.t.cpp index 084adc00..c1108911 100644 --- a/groups/ntc/ntcd/ntcd_machine.t.cpp +++ b/groups/ntc/ntcd/ntcd_machine.t.cpp @@ -92,6 +92,10 @@ class MachineTest // Concern: Sending and receiving data larger than socket buffer sizes. static void verifySendBufferOverflow(); + + // Concern: Receiving into an 'ntsa::Data' that represents a string + // having spare capacity loads the bytes that were sent. + static void verifyReceiveIntoString(); }; NTSCFG_TEST_FUNCTION(ntcd::MachineTest::verifyOpen) @@ -4319,5 +4323,129 @@ NTSCFG_TEST_FUNCTION(ntcd::MachineTest::verifySendBufferOverflow) #endif } +NTSCFG_TEST_FUNCTION(ntcd::MachineTest::verifyReceiveIntoString) +{ + NTCI_LOG_CONTEXT(); + NTCI_LOG_CONTEXT_GUARD_OWNER("main"); + + ntsa::Error error; + + // Create a machine. + + bsl::shared_ptr machine; + machine.createInplace(NTSCFG_TEST_ALLOCATOR, NTSCFG_TEST_ALLOCATOR); + + // Create a client. + + bsl::shared_ptr client = + machine->createSession(NTSCFG_TEST_ALLOCATOR); + + // Open the client as an IPv4 datagram socket. + + error = client->open(ntsa::Transport::e_UDP_IPV4_DATAGRAM); + NTSCFG_TEST_OK(error); + + // Bind the client to any port on the IPv4 loopback address. + + error = client->bind( + ntsa::Endpoint(ntsa::IpEndpoint(ntsa::Ipv4Address::loopback(), 0)), + false); + NTSCFG_TEST_OK(error); + + // Get the source endpoint of the client. + + ntsa::Endpoint clientSourceEndpoint; + error = client->sourceEndpoint(&clientSourceEndpoint); + NTSCFG_TEST_OK(error); + + // Create a server. + + bsl::shared_ptr server = + machine->createSession(NTSCFG_TEST_ALLOCATOR); + + // Open the server as an IPv4 datagram socket. + + error = server->open(ntsa::Transport::e_UDP_IPV4_DATAGRAM); + NTSCFG_TEST_OK(error); + + // Bind the server to any port on the IPv4 loopback address. + + error = server->bind( + ntsa::Endpoint(ntsa::IpEndpoint(ntsa::Ipv4Address::loopback(), 0)), + false); + NTSCFG_TEST_OK(error); + + // Get the source endpoint of the server. + + ntsa::Endpoint serverSourceEndpoint; + error = server->sourceEndpoint(&serverSourceEndpoint); + NTSCFG_TEST_OK(error); + + // Send data from the client to the server. + + const bsl::string CLIENT_DATA = "HELLOWORLD"; + + { + ntsa::Data data( + ntsa::ConstBuffer(CLIENT_DATA.data(), CLIENT_DATA.size())); + + ntsa::SendContext context; + ntsa::SendOptions options; + + options.setEndpoint(serverSourceEndpoint); + + error = client->send(&context, data, options); + NTSCFG_TEST_OK(error); + + NTSCFG_TEST_EQ(context.bytesSent(), CLIENT_DATA.size()); + } + + // Advance the simulation. + + error = machine->step(false); + NTSCFG_TEST_OK(error); + + // Receive data at the server into a string that is empty but has spare + // capacity, i.e. the destination of the received bytes is the region + // between the size and the capacity of the string. + + { + ntsa::Data data(NTSCFG_TEST_ALLOCATOR); + + bsl::string& remoteData = data.makeString(); + remoteData.reserve(64); + + NTSCFG_TEST_EQ(remoteData.size(), 0); + NTSCFG_TEST_GE(remoteData.capacity(), CLIENT_DATA.size()); + + ntsa::ReceiveContext context; + ntsa::ReceiveOptions options; + + error = server->receive(&context, &data, options); + NTSCFG_TEST_OK(error); + + NTSCFG_TEST_EQ(context.bytesReceived(), CLIENT_DATA.size()); + + // Ensure the string is the data that was sent, and not, say, the + // null bytes written by growing the string after the copy. + + NTSCFG_TEST_EQ(remoteData.size(), CLIENT_DATA.size()); + NTSCFG_TEST_EQ(remoteData, CLIENT_DATA); + + NTSCFG_TEST_FALSE(context.endpoint().isNull()); + NTSCFG_TEST_EQ(context.endpoint().value(), clientSourceEndpoint); + } + + // Close the client. + + error = client->close(); + NTSCFG_TEST_OK(error); + + // Close the server. + + error = server->close(); + NTSCFG_TEST_OK(error); +} + } // close namespace ntcd } // close namespace BloombergLP From 3765cb70af2e29e56160c0152e0d7659ad14b1d1 Mon Sep 17 00:00:00 2001 From: Evgenii Malygin <2518859+678098@users.noreply.github.com> Date: Fri, 18 Sep 2026 17:11:38 -0400 Subject: [PATCH 2/2] Fix[ntcd_machine]: resize before blob copy Avoid data corruption by resizing a buffer before writing to it. Changed 'position' to a constant for clarity and safety. Removed redundant resize call on 'data'. --- groups/ntc/ntcd/ntcd_machine.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/groups/ntc/ntcd/ntcd_machine.cpp b/groups/ntc/ntcd/ntcd_machine.cpp index f975bfa0..a369850c 100644 --- a/groups/ntc/ntcd/ntcd_machine.cpp +++ b/groups/ntc/ntcd/ntcd_machine.cpp @@ -809,11 +809,10 @@ ntsa::Error Packet::dequeueData(ntsa::ReceiveContext* context, context->reset(); - bsl::size_t position = data->size(); + const bsl::size_t position = data->size(); bsl::size_t numBytesReceivable = data->capacity() - data->size(); if (numBytesReceivable == 0) { - data->resize(d_data.length()); numBytesReceivable = d_data.length(); } @@ -824,6 +823,8 @@ ntsa::Error Packet::dequeueData(ntsa::ReceiveContext* context, numBytesToCopy = numBytesReceivable; } + data->resize(position + numBytesToCopy); + bdlbb::BlobUtil::copy(data->data() + position, d_data, 0, @@ -833,8 +834,6 @@ ntsa::Error Packet::dequeueData(ntsa::ReceiveContext* context, 0, NTCCFG_WARNING_NARROW(int, numBytesToCopy)); - data->resize(position + numBytesToCopy); - context->setEndpoint(d_sourceEndpoint); context->setBytesReceived(numBytesToCopy);