-
Notifications
You must be signed in to change notification settings - Fork 33
Fix[ntcd_machine]: data corruption and out-of-bounds write #417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The old code copied the bytes into the string first, then called So the fix is to resize the string first, then copy into it. |
||
|
|
||
| context->setEndpoint(d_sourceEndpoint); | ||
| context->setBytesReceived(numBytesToCopy); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<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); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fails in main: 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 | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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() == 4096position == 4096d_data.length() == 128This leads to
data->resize(128)and trying to write past the buffer atdata-data() + 4096