Commit 36011949 authored by Dale Curtis's avatar Dale Curtis Committed by Commit Bot

Add lock guarding AudioSourceCallback to AUHALStream.

There are a lot of crashes happening due to an invalid source, so
it appears clear that AudioUnitStop() isn't always working. This
adds a lock so that we do nothing in this case and attempts to
remove the InputProc callback during Close() to prevent UaF on the
stream itself

Fixed: 737527
Change-Id: I97f3442f105317a6214722455dab497280c07336
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2532775
Commit-Queue: Olga Sharonova <olka@chromium.org>
Reviewed-by: default avatarOlga Sharonova <olka@chromium.org>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#827775}
parent 20a3dc83
...@@ -182,6 +182,7 @@ AUHALStream::~AUHALStream() { ...@@ -182,6 +182,7 @@ AUHALStream::~AUHALStream() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
CHECK(!audio_unit_); CHECK(!audio_unit_);
base::AutoLock al(lock_);
ReportAndResetStats(); ReportAndResetStats();
} }
...@@ -208,6 +209,20 @@ bool AUHALStream::Open() { ...@@ -208,6 +209,20 @@ bool AUHALStream::Open() {
void AUHALStream::Close() { void AUHALStream::Close() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (audio_unit_) {
Stop();
// Clear the render callback to try and prevent any callbacks from coming
// in after we've called stop. https://crbug.com/737527.
AURenderCallbackStruct callback = {0};
auto result = AudioUnitSetProperty(
audio_unit_->audio_unit(), kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, AUElement::OUTPUT, &callback, sizeof(callback));
OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
<< "Failed to clear input callback.";
}
audio_unit_.reset(); audio_unit_.reset();
// Inform the audio manager that we have been closed. This will cause our // Inform the audio manager that we have been closed. This will cause our
// destruction. Also include the device ID as a signal to the audio manager // destruction. Also include the device ID as a signal to the audio manager
...@@ -225,6 +240,7 @@ void AUHALStream::Start(AudioSourceCallback* callback) { ...@@ -225,6 +240,7 @@ void AUHALStream::Start(AudioSourceCallback* callback) {
} }
if (!stopped_) { if (!stopped_) {
base::AutoLock al(lock_);
CHECK_EQ(source_, callback); CHECK_EQ(source_, callback);
return; return;
} }
...@@ -243,8 +259,12 @@ void AUHALStream::Start(AudioSourceCallback* callback) { ...@@ -243,8 +259,12 @@ void AUHALStream::Start(AudioSourceCallback* callback) {
} }
stopped_ = false; stopped_ = false;
audio_fifo_.reset();
source_ = callback; {
base::AutoLock al(lock_);
audio_fifo_.reset();
source_ = callback;
}
OSStatus result = AudioOutputUnitStart(audio_unit_->audio_unit()); OSStatus result = AudioOutputUnitStart(audio_unit_->audio_unit());
if (result == noErr) if (result == noErr)
...@@ -268,10 +288,16 @@ void AUHALStream::Stop() { ...@@ -268,10 +288,16 @@ void AUHALStream::Stop() {
OSStatus result = AudioOutputUnitStop(audio_unit_->audio_unit()); OSStatus result = AudioOutputUnitStop(audio_unit_->audio_unit());
OSSTATUS_DLOG_IF(ERROR, result != noErr, result) OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
<< "AudioOutputUnitStop() failed."; << "AudioOutputUnitStop() failed.";
if (result != noErr)
source_->OnError(AudioSourceCallback::ErrorType::kUnknown); {
ReportAndResetStats(); base::AutoLock al(lock_);
source_ = nullptr; if (result != noErr)
source_->OnError(AudioSourceCallback::ErrorType::kUnknown);
ReportAndResetStats();
source_ = nullptr;
}
stopped_ = true; stopped_ = true;
} }
...@@ -295,6 +321,13 @@ OSStatus AUHALStream::Render(AudioUnitRenderActionFlags* flags, ...@@ -295,6 +321,13 @@ OSStatus AUHALStream::Render(AudioUnitRenderActionFlags* flags,
TRACE_EVENT2("audio", "AUHALStream::Render", "input buffer size", TRACE_EVENT2("audio", "AUHALStream::Render", "input buffer size",
number_of_frames_, "output buffer size", number_of_frames); number_of_frames_, "output buffer size", number_of_frames);
base::AutoLock al(lock_);
// There's no documentation on what we should return here, but if we're here
// something is wrong so just return an AudioUnit error that looks reasonable.
if (!source_)
return kAudioUnitErr_Uninitialized;
UpdatePlayoutTimestamp(output_time_stamp); UpdatePlayoutTimestamp(output_time_stamp);
// If the stream parameters change for any reason, we need to insert a FIFO // If the stream parameters change for any reason, we need to insert a FIFO
...@@ -331,6 +364,7 @@ OSStatus AUHALStream::Render(AudioUnitRenderActionFlags* flags, ...@@ -331,6 +364,7 @@ OSStatus AUHALStream::Render(AudioUnitRenderActionFlags* flags,
} }
void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) { void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) {
lock_.AssertAcquired();
DCHECK(source_); DCHECK(source_);
const base::TimeTicks playout_time = const base::TimeTicks playout_time =
...@@ -377,6 +411,8 @@ base::TimeTicks AUHALStream::GetPlayoutTime( ...@@ -377,6 +411,8 @@ base::TimeTicks AUHALStream::GetPlayoutTime(
} }
void AUHALStream::UpdatePlayoutTimestamp(const AudioTimeStamp* timestamp) { void AUHALStream::UpdatePlayoutTimestamp(const AudioTimeStamp* timestamp) {
lock_.AssertAcquired();
if ((timestamp->mFlags & kAudioTimeStampSampleTimeValid) == 0) if ((timestamp->mFlags & kAudioTimeStampSampleTimeValid) == 0)
return; return;
...@@ -402,6 +438,8 @@ void AUHALStream::UpdatePlayoutTimestamp(const AudioTimeStamp* timestamp) { ...@@ -402,6 +438,8 @@ void AUHALStream::UpdatePlayoutTimestamp(const AudioTimeStamp* timestamp) {
} }
void AUHALStream::ReportAndResetStats() { void AUHALStream::ReportAndResetStats() {
lock_.AssertAcquired();
if (!last_sample_time_) if (!last_sample_time_)
return; // No stats gathered to report. return; // No stats gathered to report.
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "base/cancelable_callback.h" #include "base/cancelable_callback.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "media/audio/audio_io.h" #include "media/audio/audio_io.h"
...@@ -136,6 +137,9 @@ class AUHALStream : public AudioOutputStream { ...@@ -136,6 +137,9 @@ class AUHALStream : public AudioOutputStream {
const AudioParameters params_; const AudioParameters params_;
// We may get some callbacks after AudioUnitStop() has been called.
base::Lock lock_;
// Size of audio buffer requested at construction. The actual buffer size // Size of audio buffer requested at construction. The actual buffer size
// is given by |actual_io_buffer_frame_size_| and it can differ from the // is given by |actual_io_buffer_frame_size_| and it can differ from the
// requested size. // requested size.
...@@ -144,10 +148,10 @@ class AUHALStream : public AudioOutputStream { ...@@ -144,10 +148,10 @@ class AUHALStream : public AudioOutputStream {
// Stores the number of frames that we actually get callbacks for. // Stores the number of frames that we actually get callbacks for.
// This may be different from what we ask for, so we use this for stats in // This may be different from what we ask for, so we use this for stats in
// order to understand how often this happens and what are the typical values. // order to understand how often this happens and what are the typical values.
size_t number_of_frames_requested_; size_t number_of_frames_requested_ GUARDED_BY(lock_);
// Pointer to the object that will provide the audio samples. // Pointer to the object that will provide the audio samples.
AudioSourceCallback* source_; AudioSourceCallback* source_ GUARDED_BY(lock_);
// Holds the stream format details such as bitrate. // Holds the stream format details such as bitrate.
AudioStreamBasicDescription output_format_; AudioStreamBasicDescription output_format_;
...@@ -173,7 +177,7 @@ class AUHALStream : public AudioOutputStream { ...@@ -173,7 +177,7 @@ class AUHALStream : public AudioOutputStream {
// Dynamically allocated FIFO used when CoreAudio asks for unexpected frame // Dynamically allocated FIFO used when CoreAudio asks for unexpected frame
// sizes. // sizes.
std::unique_ptr<AudioPullFifo> audio_fifo_; std::unique_ptr<AudioPullFifo> audio_fifo_ GUARDED_BY(lock_);
// Current playout time. Set by Render(). // Current playout time. Set by Render().
base::TimeTicks current_playout_time_; base::TimeTicks current_playout_time_;
...@@ -191,11 +195,11 @@ class AUHALStream : public AudioOutputStream { ...@@ -191,11 +195,11 @@ class AUHALStream : public AudioOutputStream {
// These variables are only touched on the callback thread and then read // These variables are only touched on the callback thread and then read
// in the dtor (when no longer receiving callbacks). // in the dtor (when no longer receiving callbacks).
// NOTE: Float64 and UInt32 types are used for native API compatibility. // NOTE: Float64 and UInt32 types are used for native API compatibility.
Float64 last_sample_time_; Float64 last_sample_time_ GUARDED_BY(lock_);
UInt32 last_number_of_frames_; UInt32 last_number_of_frames_ GUARDED_BY(lock_);
UInt32 total_lost_frames_; UInt32 total_lost_frames_ GUARDED_BY(lock_);
UInt32 largest_glitch_frames_; UInt32 largest_glitch_frames_ GUARDED_BY(lock_);
int glitches_detected_; int glitches_detected_ GUARDED_BY(lock_);
// Used to defer Start() to workaround http://crbug.com/160920. // Used to defer Start() to workaround http://crbug.com/160920.
base::CancelableOnceClosure deferred_start_cb_; base::CancelableOnceClosure deferred_start_cb_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment