Mir

[regression] [BufferQueue] Race condition in BufferQueue::compositor_acquire could underflow shared_ptr refcount and delete prematurely, crash.

Bug #1318632 reported by Daniel van Vugt
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Mir
Fix Released
High
Alexandros Frantzis
mir (Ubuntu)
Fix Released
High
Unassigned

Bug Description

[regression] [BufferQueue] Race condition in BufferQueue::compositor_acquire could underflow shared_ptr refcount and delete prematurely, crash.

In compositor_acquire you see:

   auto const& acquired_buffer = buffer_for(current_compositor_buffer, buffers);
    if (buffer_to_release)
        release(buffer_to_release, std::move(lock));

    return acquired_buffer; // <=== shared_ptr copy constructed from acquired_ptr without any lock
}

However if the lock has been moved before the return, then it's possible two concurrent threads have the same value of acquired_buffer and will both construct shared_ptrs whose object has the same refcount (which is one too little). Later when destructed this could produce an underflow of the refcount, destroying the buffer prematurely and crashing.

I think the simple solution is to ensure you have constructed your thread's own shared_ptr result before giving up the lock. So change:
   auto const& acquired_buffer = buffer_for(current_compositor_buffer, buffers);
to:
   std::shared_ptr<mg::Buffer> acquired_buffer = buffer_for(current_compositor_buffer, buffers);

Tags: regression

Related branches

Changed in mir:
status: New → Triaged
importance: Undecided → High
milestone: none → 0.2.0
tags: added: regression
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

My understanding is that std::shared_ptr can handle the scenario described in a thread-safe manner without any external synchronization (the control block of a shared_ptr which is what is shared between instances is thread safe).

However, there may still be some races around compositor_acquire() and resizing in give_buffer_to_client() that need further investigation.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

AIUI the standard only guarantees that you can update and access two shared_ptr<>s to the same object safely in different threads.

You can't copy construct a shared_ptr<> in one thread while updating the source in another thread.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

If I've understood the discussion then:

   auto const& acquired_buffer = buffer_for(current_compositor_buffer, buffers);

should be:

   auto const acquired_buffer = buffer_for(current_compositor_buffer, buffers);

(And the RVO should elide any wasted copies.)

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> AIUI the standard only guarantees that you can update and access two shared_ptr<>s to the same object safely in different threads.

You can also create two different shared_ptr<>s from the same source in different threads (assuming a const source, of course), which is what the scenario in the bug describes.

> You can't copy construct a shared_ptr<> in one thread while updating the source in another thread.

Agreed. That's the reason for the "there may still be some races around compositor_acquire() and resizing in give_buffer_to_client() that need further investigation" comment.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

OK I misunderstood that the problem was that the reference was to a mutating pointer. As that isn't the case then there's no problem with the timing of copying the smart pointer and the description is wrong.

Changed in mir:
status: Triaged → Fix Committed
assignee: nobody → Alexandros Frantzis (afrantzis)
Changed in mir:
status: Fix Committed → Fix Released
Changed in mir (Ubuntu):
importance: Undecided → High
status: New → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.