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

[16360] Protect from uncaught exception during SHM Segment creation. (backport #3293) #3332

Merged
merged 3 commits into from
Mar 24, 2023
Merged
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
17 changes: 9 additions & 8 deletions src/cpp/rtps/DataSharing/DataSharingNotification.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,18 @@ class DataSharingNotification
{
segment_id_ = reader_guid;
segment_name_ = generate_segment_name(shared_dir, reader_guid);

uint32_t per_allocation_extra_size = T::compute_per_allocation_extra_size(
alignof(Notification), DataSharingNotification::domain_name());
uint32_t segment_size = static_cast<uint32_t>(sizeof(Notification)) + per_allocation_extra_size;

//Open the segment
T::remove(segment_name_);
std::unique_ptr<T> local_segment;

try
{
local_segment = std::unique_ptr<T>(
uint32_t per_allocation_extra_size = T::compute_per_allocation_extra_size(
alignof(Notification), DataSharingNotification::domain_name());
uint32_t segment_size = static_cast<uint32_t>(sizeof(Notification)) + per_allocation_extra_size;

//Open the segment
T::remove(segment_name_);

local_segment.reset(
new T(boost::interprocess::create_only,
segment_name_,
segment_size + T::EXTRA_SEGMENT_SIZE));
Expand Down
77 changes: 41 additions & 36 deletions src/cpp/rtps/DataSharing/WriterPool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,45 +147,50 @@ class WriterPool : public DataSharingPayloadPool
writer_ = writer;
segment_id_ = writer_->getGuid();
segment_name_ = generate_segment_name(shared_dir, segment_id_);

// We need to reserve the whole segment at once, and the underlying classes use uint32_t as size type.
// In order to avoid overflows, we will calculate using uint64 and check the casting
bool overflow = false;
size_t per_allocation_extra_size = T::compute_per_allocation_extra_size(
alignof(PayloadNode), DataSharingPayloadPool::domain_name());
size_t payload_size = DataSharingPayloadPool::node_size(max_data_size_);

uint64_t estimated_size_for_payloads_pool = pool_size_ * payload_size;
overflow |= (estimated_size_for_payloads_pool != static_cast<uint32_t>(estimated_size_for_payloads_pool));
uint32_t size_for_payloads_pool = static_cast<uint32_t>(estimated_size_for_payloads_pool);

//Reserve one extra to avoid pointer overlapping
uint64_t estimated_size_for_history = (pool_size_ + 1) * sizeof(Segment::Offset);
overflow |= (estimated_size_for_history != static_cast<uint32_t>(estimated_size_for_history));
uint32_t size_for_history = static_cast<uint32_t>(estimated_size_for_history);

uint32_t descriptor_size = static_cast<uint32_t>(sizeof(PoolDescriptor));
uint64_t estimated_segment_size = size_for_payloads_pool + per_allocation_extra_size +
size_for_history + per_allocation_extra_size +
descriptor_size + per_allocation_extra_size;
overflow |= (estimated_segment_size != static_cast<uint32_t>(estimated_segment_size));
uint32_t segment_size = static_cast<uint32_t>(estimated_segment_size);

if (overflow)
{
logError(DATASHARING_PAYLOADPOOL, "Failed to create segment " << segment_name_
<< ": Segment size is too large: " << estimated_size_for_payloads_pool
<< " (max is " << std::numeric_limits<uint32_t>::max() << ")."
<< " Please reduce the maximum size of the history");
return false;
}

//Open the segment
T::remove(segment_name_);
std::unique_ptr<T> local_segment;
size_t payload_size;
uint64_t estimated_size_for_payloads_pool;
uint64_t estimated_size_for_history;
uint32_t size_for_payloads_pool;

try
{
local_segment = std::unique_ptr<T>(
// We need to reserve the whole segment at once, and the underlying classes use uint32_t as size type.
// In order to avoid overflows, we will calculate using uint64 and check the casting
bool overflow = false;
size_t per_allocation_extra_size = T::compute_per_allocation_extra_size(
alignof(PayloadNode), DataSharingPayloadPool::domain_name());
payload_size = DataSharingPayloadPool::node_size(max_data_size_);

estimated_size_for_payloads_pool = pool_size_ * payload_size;
overflow |= (estimated_size_for_payloads_pool != static_cast<uint32_t>(estimated_size_for_payloads_pool));
size_for_payloads_pool = static_cast<uint32_t>(estimated_size_for_payloads_pool);

//Reserve one extra to avoid pointer overlapping
estimated_size_for_history = (pool_size_ + 1) * sizeof(Segment::Offset);
overflow |= (estimated_size_for_history != static_cast<uint32_t>(estimated_size_for_history));
uint32_t size_for_history = static_cast<uint32_t>(estimated_size_for_history);

uint32_t descriptor_size = static_cast<uint32_t>(sizeof(PoolDescriptor));
uint64_t estimated_segment_size = size_for_payloads_pool + per_allocation_extra_size +
size_for_history + per_allocation_extra_size +
descriptor_size + per_allocation_extra_size;
overflow |= (estimated_segment_size != static_cast<uint32_t>(estimated_segment_size));
uint32_t segment_size = static_cast<uint32_t>(estimated_segment_size);

if (overflow)
{
logError(DATASHARING_PAYLOADPOOL, "Failed to create segment " << segment_name_
<< ": Segment size is too large: " << estimated_size_for_payloads_pool
<< " (max is " << std::numeric_limits<uint32_t>::max() << ")."
<< " Please reduce the maximum size of the history");
return false;
}

//Open the segment
T::remove(segment_name_);

local_segment.reset(
new T(boost::interprocess::create_only,
segment_name_,
segment_size + T::EXTRA_SEGMENT_SIZE));
Expand Down
18 changes: 15 additions & 3 deletions src/cpp/rtps/transport/shared_mem/SharedMemManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,21 @@ class SharedMemManager :
" characters");
}

uint32_t extra_size =
SharedMemSegment::compute_per_allocation_extra_size(std::alignment_of<BufferNode>::value, domain_name);
return std::shared_ptr<SharedMemManager>(new SharedMemManager(domain_name, extra_size));
try
{
uint32_t extra_size =
SharedMemSegment::compute_per_allocation_extra_size(std::alignment_of<BufferNode>::value,
domain_name);
return std::shared_ptr<SharedMemManager>(new SharedMemManager(domain_name, extra_size));
}
catch (const std::exception& e)
{
logError(RTPS_TRANSPORT_SHM, "Failed to create Shared Memory Manager for domain " << domain_name
<< ": " <<
e.what());
return std::shared_ptr<SharedMemManager>();
}

}

~SharedMemManager()
Expand Down
4 changes: 4 additions & 0 deletions src/cpp/rtps/transport/shared_mem/SharedMemTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ bool SharedMemTransport::init(
try
{
shared_mem_manager_ = SharedMemManager::create(SHM_MANAGER_DOMAIN);
if (!shared_mem_manager_)
{
return false;
}
shared_mem_segment_ = shared_mem_manager_->create_segment(configuration_.segment_size(),
configuration_.port_queue_capacity());

Expand Down
2 changes: 1 addition & 1 deletion src/cpp/utils/shared_memory/SharedMemSegment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ class SharedSegment : public SharedSegmentBase
}

/**
* Estimates the extra segment space required for an allocation
* Estimates the extra segment space required for an allocation. This may throw
*/
static uint32_t compute_per_allocation_extra_size(
size_t allocation_alignment,
Expand Down
Loading