Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions groups/ntc/ntcd/ntcd_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

@678098 678098 Sep 18, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was also a problem here. When the string had no spare room at all, the old code tried to make room with resize(d_data.length()) - but that might shrink the string rather than enlarging its buffer, so the copy ran off the end of the allocated memory.

For example, with
data->capacity() == data->size() == 4096
position == 4096
d_data.length() == 128

This leads to data->resize(128) and trying to write past the buffer at data-data() + 4096

numBytesReceivable = d_data.length();
}

Expand All @@ -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,
Expand All @@ -833,8 +834,6 @@ ntsa::Error Packet::dequeueData(ntsa::ReceiveContext* context,
0,
NTCCFG_WARNING_NARROW(int, numBytesToCopy));

data->resize(position + numBytesToCopy);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old code copied the bytes into the string first, then called resize() to make the string long enough. But growing a string fills the new space with zeros — so it immediately erased the bytes it had just copied. The caller got the right byte count but all zeros, and the packet had already been discarded, so the data was gone for good.

So the fix is to resize the string first, then copy into it.


context->setEndpoint(d_sourceEndpoint);
context->setBytesReceived(numBytesToCopy);

Expand Down
128 changes: 128 additions & 0 deletions groups/ntc/ntcd/ntcd_machine.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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<ntcd::Machine> machine;
machine.createInplace(NTSCFG_TEST_ALLOCATOR, NTSCFG_TEST_ALLOCATOR);

// Create a client.

bsl::shared_ptr<ntcd::Session> 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<ntcd::Session> 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);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fails in main:

 684/1013 Test  #684: ntc ntcd::MachineTest::verifyReceiveIntoString ......................................Subprocess aborted***Exception:   0.13 sec
[ 2026-09-18T20:58:51.514384Z ][ FATAL ][ 7F939D8A6780 ][ ntcd_machine.t.cpp:4433 ][ BSLS.LOG ]:
Assertion failed: remoteData == CLIENT_DATA
Found:     (remoteData)
Expected: HELLOWORLD (CLIENT_DATA) 
99% tests passed, 1 tests failed out of 684
Total Test time (real) =  67.36 sec

https://github.com/bloomberg/ntf-core/actions/runs/35394079728/job/105758779335?pr=417


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
Loading