Skip to content

Commit a801a98

Browse files
test: cover rmw_take_sequence mid-batch deserialize failure
Inject [good, corrupt, good] datagrams into a subscription socket and assert take_sequence yields two contiguous messages (size == *taken == 2) with no hole and nothing lost. Fails before the cursor fix (data[1] was a hole and the second good message, written at data[2], was dropped).
1 parent cad8d0f commit a801a98

1 file changed

Lines changed: 87 additions & 0 deletions

File tree

‎rmw_unix_socket_cpp/test/test_rmw_pub_sub.cpp‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88
#include "rmw/qos_profiles.h"
99
#include "rosidl_typesupport_cpp/message_type_support.hpp"
1010

11+
#include <chrono>
12+
#include <thread>
13+
#include <vector>
14+
15+
#include <unistd.h>
16+
17+
#include "../src/types.hpp"
18+
#include "../src/transport.hpp"
19+
#include "../src/serialization.hpp"
20+
1121
class PubSubTest : public RmwUdsNodeTest
1222
{
1323
protected:
@@ -200,3 +210,80 @@ TEST_F(PubSubTest, StringMessages)
200210
ASSERT_TRUE(taken);
201211
EXPECT_EQ("Hello from Unix sockets!", recv_msg.string_value);
202212
}
213+
214+
// Regression for the rmw_take_sequence cursor bug: a deserialize failure in the
215+
// middle of a batch must not leave an uninitialized hole or miscount size — each
216+
// success is written at the *taken cursor, not the loop index. We inject
217+
// [good, corrupt, good] straight into the subscription's datagram socket; a
218+
// single sender over SOCK_DGRAM preserves datagram order, so the corrupt one
219+
// lands between the two good ones. Before the fix the second good message was
220+
// written at data[2] and lost, while size == *taken == 2 exposed data[1] (the
221+
// hole left by the failed deserialize) to the consumer as a valid message.
222+
TEST_F(PubSubTest, TakeSequenceSkipsMidBatchCorruptContiguously)
223+
{
224+
auto sub_opts = rmw_get_default_subscription_options();
225+
sub = rmw_create_subscription(node, ts, "/take_seq_corrupt", &qos, &sub_opts);
226+
ASSERT_NE(nullptr, sub);
227+
228+
auto * sub_data = static_cast<rmw_uds::UdsSubscription *>(sub->data);
229+
ASSERT_FALSE(sub_data->socket_path.empty());
230+
ASSERT_NE(nullptr, sub_data->callbacks);
231+
232+
// A valid serialized BasicTypes payload (deserializes successfully).
233+
test_msgs::msg::BasicTypes good;
234+
good.int32_value = 4242;
235+
good.bool_value = true;
236+
std::vector<uint8_t> good_payload;
237+
ASSERT_TRUE(rmw_uds::serialize(&good, sub_data->callbacks, good_payload));
238+
239+
int send_fd = rmw_uds::create_send_socket();
240+
ASSERT_GE(send_fd, 0);
241+
242+
auto inject = [&](const uint8_t * data, size_t len) {
243+
rmw_uds::WireHeader hdr;
244+
std::memset(&hdr, 0, sizeof(hdr));
245+
hdr.payload_size = static_cast<uint32_t>(len);
246+
hdr.msg_type = 0; // topic message
247+
ASSERT_TRUE(rmw_uds::send_to(send_fd, sub_data->socket_path, hdr, data, len));
248+
};
249+
250+
// good, corrupt (far too short to deserialize as BasicTypes), good.
251+
const uint8_t corrupt[4] = {0xDE, 0xAD, 0xBE, 0xEF};
252+
inject(good_payload.data(), good_payload.size());
253+
inject(corrupt, sizeof(corrupt));
254+
inject(good_payload.data(), good_payload.size());
255+
256+
// Datagrams are buffered by the kernel on send; a short pause is defensive.
257+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
258+
259+
// Take all three at once: the corrupt one is skipped, leaving two contiguous
260+
// good messages with size == *taken == 2 (no hole, nothing lost).
261+
test_msgs::msg::BasicTypes out[3];
262+
void * out_ptrs[3] = {&out[0], &out[1], &out[2]};
263+
rmw_message_sequence_t seq;
264+
seq.data = out_ptrs;
265+
seq.size = 0;
266+
seq.capacity = 3;
267+
seq.allocator = nullptr;
268+
269+
rmw_message_info_t infos[3];
270+
std::memset(infos, 0, sizeof(infos));
271+
rmw_message_info_sequence_t info_seq;
272+
info_seq.data = infos;
273+
info_seq.size = 0;
274+
info_seq.capacity = 3;
275+
info_seq.allocator = nullptr;
276+
277+
size_t taken = 99;
278+
EXPECT_EQ(
279+
RMW_RET_OK,
280+
rmw_take_sequence(sub, 3, &seq, &info_seq, &taken, nullptr));
281+
282+
EXPECT_EQ(2u, taken);
283+
EXPECT_EQ(2u, seq.size);
284+
EXPECT_EQ(2u, info_seq.size);
285+
EXPECT_EQ(4242, out[0].int32_value);
286+
EXPECT_EQ(4242, out[1].int32_value); // the bug left this a hole and lost it
287+
288+
close(send_fd);
289+
}

0 commit comments

Comments
 (0)