Bounds-check EventHandlerBase::is_ready() to fix out-of-bounds read (#2376) - #3170
Conversation
7aa7462 to
048bc4d
Compare
|
Tick the box to add this pull request to the merge queue (same as
|
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
If add_to_wait_set() and is_ready() can genuinely race as described, the bounds check fixes the OOB symptom but wait_set_event_index_ is still read and written concurrently without synchronization. Should the index be atomic, or should the lifecycle guarantee be enforced another way?
…2376) is_ready() indexed wait_set.events with wait_set_event_index_, which is only assigned by add_to_wait_set(). If is_ready() runs before that the member is uninitialized; if it runs against a different wait set the index belongs to that other one. Either way the access reads out of bounds and can segfault (observed with ASan during lifecycle configure). Initialize wait_set_event_index_ and make is_ready() return false when the index is not within the wait set's events instead of dereferencing it. Initializing alone is not sufficient (index 0 may still not be this handler's slot), so the bounds check is the actual guard. Adds two regression tests: one calling is_ready() on a handler that was never added to a wait set, and one where the index was assigned by a different wait set and equals size_of_events. Refs ros2#2376 Co-authored-by: Mike Wake <michael.wake@aosgrp.com.au> Generated-by: Claude Opus 4.8 (Anthropic) Signed-off-by: Pavel Guzenfeld <pavelguzenfeld@gmail.com> Signed-off-by: Mike Wake <michael.wake@aosgrp.com.au>
048bc4d to
defb18b
Compare
|
Thanks for the review. There's no actual race: p.s, Also added a test for the index == size_of_events boundary. |
EventHandlerBase::is_ready()indexeswait_set.events[wait_set_event_index_], but that index is only assigned byadd_to_wait_set(). Ifis_ready()runs before that the member is uninitialized; if it runs against a different wait set the index belongs to that other one. Either way the access reads out of bounds and can segfault (observed with ASan during a lifecycleconfigure).This initializes
wait_set_event_index_to an always-out-of-range sentinel and makesis_ready()return false when the index is not within the wait set's events, instead of dereferencing it.Adds two regression tests: one calling
is_ready()on a handler that was never added to a wait set, and one where the index was assigned by a different wait set and equalssize_of_events.Builds on @aosmw's work in #2376; per @clalancette's review there, initializing the index alone is not sufficient (it could still point at another slot), so the bounds check in
is_ready()is the actual guard.Some of this change was produced with Claude Opus 4.8 (Anthropic).