Skip to content

fix(opcua): thread-safety fix for the REST/poll fault-buffer race#519

Open
mfaferek93 wants to merge 2 commits into
mainfrom
fix/opcua-thread-safety
Open

fix(opcua): thread-safety fix for the REST/poll fault-buffer race#519
mfaferek93 wants to merge 2 commits into
mainfrom
fix/opcua-thread-safety

Conversation

@mfaferek93

Copy link
Copy Markdown
Collaborator

Problem

Under UI load the OPC-UA gateway crashed with heap-corruption signatures (GPF /
SIGILL / bad-pointer segfault) inside libros2_medkit_opcua_plugin.so. Root
cause is a data race on pending_reports_, the fire-and-forget fault
report/clear buffer: it is mutated by the poll thread (publish_values,
on_alarm_change/on_event_alarm) AND by the REST thread
(FaultProvider::clear_fault -> send_clear_fault -> send_or_buffer) with no
lock. A push_back that reallocates the vector while the other thread iterates
it is a use-after-free that corrupts the heap.

Fix

Guard every access with a mutex. flush_pending_reports swaps the ready batch
out under the lock and dispatches it outside, so the vector is never reallocated
mid-iteration and the ROS service call never runs under the lock.

Test

OpcuaPluginConcurrency.ClearFaultBufferIsThreadSafe: six threads drive the
REST clear_fault path against the running poll thread. It segfaults on the
unsynchronized code (verified) and passes with the fix. Full unit suite green.

Copilot AI review requested due to automatic review settings July 10, 2026 20:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses a confirmed data race in the OPC-UA plugin’s buffered fault report/clear dispatch queue (pending_reports_) that could trigger heap corruption when the poll thread and REST thread concurrently mutate/iterate the vector.

Changes:

  • Add a mutex (pending_reports_mutex_) to serialize all access to pending_reports_.
  • Refactor flush_pending_reports() to swap the pending batch under lock and dispatch outside the critical section.
  • Add a concurrency regression test that stresses the REST clear_fault path against the running poll thread.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/ros2_medkit_plugins/ros2_medkit_opcua/src/opcua_plugin.cpp Serializes pending dispatch buffering and flush via a mutex; flush now swaps-and-dispatches outside the lock.
src/ros2_medkit_plugins/ros2_medkit_opcua/include/ros2_medkit_opcua/opcua_plugin.hpp Documents the two-thread access pattern and introduces pending_reports_mutex_ to protect the buffer.
src/ros2_medkit_plugins/ros2_medkit_opcua/test/test_opcua_plugin.cpp Adds a multi-threaded regression test to reproduce/guard the REST-vs-poll buffer race.

FaultProvider::clear_fault (REST thread) and the poll thread both mutated
pending_reports_ with no lock; a reallocating push_back while the other thread
iterated it corrupted the heap and crashed the gateway under load. Guard every
access with a mutex and dispatch a swapped-out batch outside the lock. Adds a
concurrency regression test that reproduces the crash without the fix.
ClearFaultBufferIsThreadSafe called rclcpp::init() with no matching
shutdown, leaking global ROS state into the rest of the test process.
Wrap it in a ScopedRclcpp RAII guard that tears down only the init it
performed, so shutdown runs even if an assertion returns early.
@mfaferek93 mfaferek93 force-pushed the fix/opcua-thread-safety branch from 71a37dd to 4aeb17b Compare July 11, 2026 12:20
@bburda bburda self-requested a review July 11, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants