Skip to content

Make sure FilterAudioStream is alive in callbacks (#2325)#2354

Open
jg-hot wants to merge 1 commit intogoogle:mainfrom
jg-hot:FilterAudioStream-fix
Open

Make sure FilterAudioStream is alive in callbacks (#2325)#2354
jg-hot wants to merge 1 commit intogoogle:mainfrom
jg-hot:FilterAudioStream-fix

Conversation

@jg-hot
Copy link
Copy Markdown

@jg-hot jg-hot commented Mar 21, 2026

This modifies AAudioStreamCollection.getStream() to return a tuple which includes a shared_ptr to the parent stream if the AAudioStream is wrapped by a FilterAudioStream.

The child is linked to the parent via a raw pointer (AudioStream.getParentStream() / AudioStream.hasParentStream() and locked during callbacks with lockWeakThis().

The shared_ptr to the parent is passed to the callback threads (e.g. oboe_aaudio_error_thread_proc_shared) so its memory is retained until the callback thread finishes running.

See discussion under issue #2325 for more details.

Tested against the sample in FilterAudioStreamUAF-repro.

@jg-hot
Copy link
Copy Markdown
Author

jg-hot commented Mar 21, 2026

@flamme @robertwu1 could you please review this when you get a chance? I'm not able to add reviewers directly. Thanks.

@robertwu1 robertwu1 requested review from flamme and robertwu1 March 23, 2026 18:48
@robertwu1
Copy link
Copy Markdown
Collaborator

LGTM @flamme please add comments if you have any. Thanks!

// callbacks are routed through it.
std::shared_ptr<AudioStream> sharedParentStream;
if (sharedStream && sharedStream->hasParentStream()) {
sharedParentStream = sharedStream->getParentStream()->lockWeakThis();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Q: this parent stream is a raw pointer, how it guarantees the parent stream is still alive when a callback arrive?

Copy link
Copy Markdown
Author

@jg-hot jg-hot Mar 27, 2026

Choose a reason for hiding this comment

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

This relies on the same assumption that the AAudioStreamCollection uses for the child (AAudio) stream. That the client always calls close() before releasing their shared_ptr.

That ensures that callbacks are rejected if the stream is about to be released.

The parent should be alive as long as the child is. The ownership chain is shared_ptr client → FilterAudioStreamAudioStreamAAudio.

I don't see any case where the parent is freed but the child is not.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Isn't the root cause that the parent stream is gone while the child stream is still alive? The child stream received an error callback and call to parent's error callback, which was swapped in FilterAudioStream constructor.

Copy link
Copy Markdown
Author

@jg-hot jg-hot Apr 2, 2026

Choose a reason for hiding this comment

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

In #2325 the parent is still alive at the moment of the callback (AudioStreamAAudio::internalErrorCallback(), call to getStream() here).

If what you're describing is possible (parent freed, but child is still alive in internalErrorCallback()), that would be a separate bug (but as mentioned above that shouldn't happen due to the ownership chain and AAudioStreamCollection returning isStreamAlive).

The root cause of the original bug is as follows (race condition):

  • internalErrorCallback() runs. Memory is locked (only the child, before applying this fix), then a std::thread is created.
  • Client releases their shared_ptr to the stream
  • At some point in the future the thread is scheduled for execution and crashes here since only the child was retained.

The core idea of this fix is to simply pass an additional shared_ptr (parent) to the thread struct for oboe_aaudio_error_thread_proc_shared to fix the race. I.e. adding sharedParentStream here.

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.

3 participants