Skip to content
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

Fix leak if client reponse is never taken #201

Merged
merged 3 commits into from
Jun 1, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#include <atomic>
#include <list>
#include <memory>
#include <utility>

#include "fastcdr/FastBuffer.h"

Expand All @@ -43,7 +45,7 @@ typedef struct CustomClientInfo
typedef struct CustomClientResponse
{
eprosima::fastrtps::rtps::SampleIdentity sample_identity_;
eprosima::fastcdr::FastBuffer * buffer_;
std::unique_ptr<eprosima::fastcdr::FastBuffer> buffer_;
Copy link
Contributor

Choose a reason for hiding this comment

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

why does this have to be a heap allocation in the first place?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I hadn't thought of that. It shouldn't need to be. I just tried changing it to an instance, but I can't get around double-free errors.

That class uses raw pointers, but it does not implement a copy constructor or move constructor nor does it delete them. As a result the same pointer is freed when CustomClientResponse is removed from the list and when CustomClientResponse is destructed. I'll make an issue on the FastCDR repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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


CustomClientResponse()
: buffer_(nullptr) {}
Expand All @@ -63,10 +65,10 @@ class ClientListener : public eprosima::fastrtps::SubscriberListener
assert(sub);

CustomClientResponse response;
response.buffer_ = new eprosima::fastcdr::FastBuffer();
response.buffer_.reset(new eprosima::fastcdr::FastBuffer());
eprosima::fastrtps::SampleInfo_t sinfo;

if (sub->takeNextData(response.buffer_, &sinfo)) {
if (sub->takeNextData(response.buffer_.get(), &sinfo)) {
if (eprosima::fastrtps::rtps::ALIVE == sinfo.sampleKind) {
response.sample_identity_ = sinfo.related_sample_identity;

Expand All @@ -75,15 +77,15 @@ class ClientListener : public eprosima::fastrtps::SubscriberListener

if (conditionMutex_ != nullptr) {
std::unique_lock<std::mutex> clock(*conditionMutex_);
list.push_back(response);
list.emplace_back(std::move(response));
// the change to list_has_data_ needs to be mutually exclusive with
// rmw_wait() which checks hasData() and decides if wait() needs to
// be called
list_has_data_.store(true);
clock.unlock();
conditionVariable_->notify_one();
} else {
list.push_back(response);
list.emplace_back(std::move(response));
list_has_data_.store(true);
}
}
Expand All @@ -100,19 +102,19 @@ class ClientListener : public eprosima::fastrtps::SubscriberListener
if (conditionMutex_ != nullptr) {
std::unique_lock<std::mutex> clock(*conditionMutex_);
if (!list.empty()) {
response = list.front();
response = std::move(list.front());
Copy link
Contributor

@Karsten1987 Karsten1987 May 15, 2018

Choose a reason for hiding this comment

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

this move is necessary because of the unique_ptr inside the response, right? This is potentially dangerous because the list is afterwards invalid.
If you weren't calling pop_front() immediately afterwards (which you do!), and then try to access the first element of the list again, this would segfault.

Copy link
Contributor Author

@sloretz sloretz May 15, 2018

Choose a reason for hiding this comment

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

210346b

I don't see an API in the list class that combines getting the first element and removing it.
210346b uses a lambda so at least all the risky stuff is in one spot. It should be fine as long as the move constructor assignment operator doesn't throw an exception.

list.pop_front();
list_has_data_.store(!list.empty());
}
} else {
if (!list.empty()) {
response = list.front();
response = std::move(list.front());
list.pop_front();
list_has_data_.store(!list.empty());
}
}

return response;
return std::move(response);
}

void
Expand Down
4 changes: 1 addition & 3 deletions rmw_fastrtps_cpp/src/rmw_response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,12 @@ rmw_take_response(
CustomClientResponse response = info->listener_->getResponse();

if (response.buffer_ != nullptr) {
_deserialize_ros_message(response.buffer_, ros_response, info->response_type_support_,
_deserialize_ros_message(response.buffer_.get(), ros_response, info->response_type_support_,
info->typesupport_identifier_);

request_header->sequence_number = ((int64_t)response.sample_identity_.sequence_number().high) <<
32 | response.sample_identity_.sequence_number().low;

delete response.buffer_;

*taken = true;
}

Expand Down