ros2 / ros2/rclcpp

Consider protecting Context (pre-)shutdown callback interface against use-after-frees

Open
#2,099 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
805
Forks
564
Avg merge
1d 17h
Merged PRs (30d)
27

Description

Bug report

Required Info:

  • Operating System:
    • All
  • Installation type:
    • All
  • Version or commit hash:
    • HEAD
  • DDS implementation:
    • ALl
  • Client library (if applicable):
    • rclcpp
Steps to reproduce issue

It's possible to create a Context shutdown callback using an std::bind to a class instance. If that instance is freed, the callback
can later fire and seg fault.

Example:

    context->add_pre_shutdown_callback(
      std::bind(&LifecycleNode::on_rcl_preshutdown, this))

Feature request

Feature description

To be sure, the user should not allow the instance to be freed until the callback is removed. They could e.g. add the cb removal in the destructor.

@SteveMacenski recommended we consider a pattern on the callback side to try preventing segfaults when somebody makes such mistakes, or to make the mistake clear via e.g. an exception. As such, this is a feature request towards that end.

We may be able to make another interface where a different type of cb handle is returned. That new type automatically removes the cb in its destructor. It holds a ptr back to the callback handle we are using today today. Then the user would need to keep the handle around, and it could be removed in the automatic destruction process of an instance containing it (rather than needing to update dtor code to do it, since the user may mistakenly forget). If the user fails to keep the this new type of handle around, the dtor will fire automatically and the cb will never be used (which should be pretty obvious).

A clearer protection may be something like add_pre_shutdown_callback_safe where the user passes a shared_ptr to the ShutdownCallbackType type object instead of us making a new shared_ptr in the Context code (see Context::add_shutdown_callback), and to check the corresponding weak_ptr for expiration. If it expires before we call the callback, we raise an exception or log instead of calling the now-freed thing.

Anyways, consider whether we want to make some segfault protections here somehow.

Expected behavior

No seg faults in the shutdown callback path.

Actual behavior

Context shutdown callback ptr can point to freed thing, causing segfaults.

Additional information

Implementation considerations

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading Context::add_shutdown_callback and the add_pre_shutdown_callback usage with std::bind to understand callback lifetime and removal. Compare the proposed automatic handle and shared_ptr/weak_ptr approaches, then define an agreed interface whose shutdown path cannot call a freed instance and document or test the expected behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
api, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.