Skip to content

Commit 22a656d

Browse files
committed
Fix CVE-2026-22590
This commit fixes CVE-2026-22590 by always checking the incoming data length in `CacheChange::add_fragments()`. This is a squashed commit of a privately reviewed branch. Signed-off-by: Miguel Company <miguelcompany@eprosima.com> Reviewed-by: Ricardo González Moreno <ricardo@richiware.dev>
1 parent a455147 commit 22a656d

File tree

2 files changed

+104
-8
lines changed

2 files changed

+104
-8
lines changed

include/fastdds/rtps/common/CacheChange.hpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -294,17 +294,16 @@ struct FASTDDS_EXPORTED_API CacheChange_t
294294
return false;
295295
}
296296

297-
// validate lengths
298-
if (last_fragment_index < fragment_count_)
297+
// Update incoming length for last fragment
298+
if (last_fragment_index == fragment_count_)
299299
{
300-
if (incoming_data.length < incoming_length)
301-
{
302-
return false;
303-
}
300+
incoming_length = serializedPayload.length - original_offset;
304301
}
305-
else
302+
303+
// Validate lengths
304+
if (incoming_data.length < incoming_length)
306305
{
307-
incoming_length = serializedPayload.length - original_offset;
306+
return false;
308307
}
309308

310309
if (original_offset + incoming_length > serializedPayload.length)

test/blackbox/common/BlackboxTestsTransportUDP.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,103 @@ TEST(TransportUDP, MaliciousDataFragUnalignedSizes)
989989
reader.block_for_all();
990990
}
991991

992+
// Regression test for redmine issue #24030
993+
TEST(TransportUDP, MaliciousDataFragLastFragment)
994+
{
995+
// Force using UDP transport
996+
auto udp_transport = std::make_shared<UDPv4TransportDescriptor>();
997+
998+
PubSubWriter<UnboundedHelloWorldPubSubType> writer(TEST_TOPIC_NAME);
999+
PubSubReader<UnboundedHelloWorldPubSubType> reader(TEST_TOPIC_NAME);
1000+
1001+
struct MaliciousDataFragInconsistentLastFragmentLength
1002+
{
1003+
std::array<char, 4> rtps_id{ {'R', 'T', 'P', 'S'} };
1004+
std::array<uint8_t, 2> protocol_version{ {2, 3} };
1005+
std::array<uint8_t, 2> vendor_id{ {0x01, 0x0F} };
1006+
GuidPrefix_t sender_prefix{};
1007+
1008+
struct DataFragSubMsg
1009+
{
1010+
struct Header
1011+
{
1012+
uint8_t submessage_id = 0x16;
1013+
#if FASTDDS_IS_BIG_ENDIAN_TARGET
1014+
uint8_t flags = 0x00;
1015+
#else
1016+
uint8_t flags = 0x01;
1017+
#endif // FASTDDS_IS_BIG_ENDIAN_TARGET
1018+
uint16_t octets_to_next_header = 0x24;
1019+
uint16_t extra_flags = 0;
1020+
uint16_t octets_to_inline_qos = 0x1c;
1021+
EntityId_t reader_id{};
1022+
EntityId_t writer_id{};
1023+
SequenceNumber_t sn{100};
1024+
uint32_t fragment_starting_num = 1;
1025+
uint16_t fragments_in_submessage = 8;
1026+
uint16_t fragment_size = 65504;
1027+
uint32_t sample_size = 65504 * 8;
1028+
};
1029+
1030+
struct SerializedData
1031+
{
1032+
octet data[4] {0xA0, 0xA1, 0xA2, 0xA3};
1033+
};
1034+
1035+
Header header;
1036+
SerializedData payload;
1037+
}
1038+
data;
1039+
};
1040+
1041+
UDPMessageSender fake_msg_sender;
1042+
1043+
// Set common QoS
1044+
reader.disable_builtin_transport().add_user_transport_to_pparams(udp_transport)
1045+
.history_depth(10).reliability(eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS);
1046+
writer.history_depth(10).reliability(eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS);
1047+
1048+
// Set custom reader locator so we can send malicious data to a known location
1049+
Locator_t reader_locator;
1050+
ASSERT_TRUE(IPLocator::setIPv4(reader_locator, "127.0.0.1"));
1051+
reader_locator.port = 7000;
1052+
reader.add_to_unicast_locator_list("127.0.0.1", 7000);
1053+
1054+
// Initialize and wait for discovery
1055+
reader.init();
1056+
ASSERT_TRUE(reader.isInitialized());
1057+
writer.init();
1058+
ASSERT_TRUE(writer.isInitialized());
1059+
1060+
reader.wait_discovery();
1061+
writer.wait_discovery();
1062+
1063+
auto data = default_unbounded_helloworld_data_generator();
1064+
reader.startReception(data);
1065+
writer.send(data);
1066+
ASSERT_TRUE(data.empty());
1067+
1068+
// Send malicious data
1069+
{
1070+
auto writer_guid = writer.datawriter_guid();
1071+
1072+
MaliciousDataFragInconsistentLastFragmentLength malicious_packet{};
1073+
malicious_packet.sender_prefix = writer_guid.guidPrefix;
1074+
malicious_packet.data.header.writer_id = writer_guid.entityId;
1075+
malicious_packet.data.header.reader_id = reader.datareader_guid().entityId;
1076+
1077+
CDRMessage_t msg(0);
1078+
uint32_t msg_len = static_cast<uint32_t>(sizeof(malicious_packet));
1079+
msg.init(reinterpret_cast<octet*>(&malicious_packet), msg_len);
1080+
msg.length = msg_len;
1081+
msg.pos = msg_len;
1082+
fake_msg_sender.send(msg, reader_locator);
1083+
}
1084+
1085+
// Block reader until reception finished or timeout.
1086+
reader.block_for_all();
1087+
}
1088+
9921089
// Regression test for redmine issue #23917
9931090
TEST(TransportUDP, MaliciousHeartbeatBigStart)
9941091
{

0 commit comments

Comments
 (0)