Bug 172101 - Crash under WebCore::AudioSourceProviderAVFObjC::process().
Summary: Crash under WebCore::AudioSourceProviderAVFObjC::process().
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore Misc. (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Per Arne Vollan
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2017-05-14 23:22 PDT by Per Arne Vollan
Modified: 2017-05-17 14:53 PDT (History)
7 users (show)

See Also:


Attachments
Patch (1.77 KB, patch)
2017-05-15 00:24 PDT, Per Arne Vollan
no flags Details | Formatted Diff | Diff
Patch (2.66 KB, patch)
2017-05-16 02:56 PDT, Per Arne Vollan
no flags Details | Formatted Diff | Diff
Patch (2.67 KB, patch)
2017-05-16 03:03 PDT, Per Arne Vollan
no flags Details | Formatted Diff | Diff
Patch (3.11 KB, patch)
2017-05-16 09:47 PDT, Per Arne Vollan
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Per Arne Vollan 2017-05-14 23:22:19 PDT
WebCore::AudioSourceProviderAVFObjC::process() is calling MTAudioProcessingTapGetSourceAudio() where the value of the parameter MTAudioProcessingTapRef is NULL.

Crashed Thread:        29  AUDeferredRenderer-0x7fe86d4e20

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000010
Exception Note:        EXC_CORPSE_NOTIFY

Thread 29 Crashed:: AUDeferredRenderer-0x7fe86d4e20
0   com.apple.MediaToolbox        	0x7fffa8aabb53 MTAudioProcessingTapGetSourceAudio + 129 (/Library/Caches/com.apple.xbs/Sources/CoreMedia/CoreMedia-1873/Prototypes/MediaConverter/MTAudioProcessingTap.c:869)
1   com.apple.WebCore             	0x00007fffac05b580 WebCore::AudioSourceProviderAVFObjC::process(long, unsigned int, AudioBufferList*, long*, unsigned int*) + 64
2   com.apple.MediaToolbox        	0x7fffa8aacaab aptap_AudioQueueProcessingTapCallback + 158 (/Library/Caches/com.apple.xbs/Sources/CoreMedia/CoreMedia-1873/Prototypes/MediaConverter/MTAudioProcessingTap.c:466)
3   com.apple.audio.toolbox.AudioToolbox	0x00007fffa450c97f AQClientCallProcessingTap(void*, unsigned int, AudioTimeStamp*, unsigned int*, unsigned int*, AudioBufferList*, int, double, unsigned int, double, unsigned int, unsigned char const*, unsigned int) + 93
4   com.apple.audio.toolbox.AudioToolbox	0x00007fffa45934b0 AQProcessingTap::DoCallout(unsigned int&, AudioTimeStamp&, unsigned int&, AudioBufferList&) + 188
5   com.apple.audio.toolbox.AudioToolbox	0x00007fffa45932af AudioQueueObject::PerformTapInternal(AudioTimeStamp&, unsigned int&, unsigned int&) + 133
6   com.apple.audio.toolbox.AudioToolbox	0x00007fffa45931b5 AudioQueueObject::PerformProcessingTap(int (*)(void*, unsigned int&, AudioTimeStamp const&, unsigned int, AudioBufferList&, double&, unsigned int&), void*, AudioTimeStamp&, unsigned int&, AudioBufferList&, unsigned int&) + 197
7   com.apple.audio.toolbox.AudioToolbox	0x00007fffa459ddd9 MEMixerChannel::TapDownstream(void*, unsigned int*, AudioTimeStamp const*, unsigned int, unsigned int, AudioBufferList*) + 129
8   com.apple.audio.units.Components	0x000000011a51c6b5 AUInputElement::PullInput(unsigned int&, AudioTimeStamp const&, unsigned int, unsigned int) + 185
9   com.apple.audio.units.Components	0x000000011a45f374 AUDeferredRenderer::Produce() + 576
10  com.apple.audio.units.Components	0x000000011a45f0ab AUDeferredRenderer::ProducerThreadProc() + 49
11  com.apple.audio.units.Components	0x000000011a45eaab AUDeferredRenderer::ProducerThreadProc(void*) + 31
12  com.apple.audio.units.Components	0x000000011a532737 CAPThread::Entry(CAPThread*) + 85
13  libsystem_pthread.dylib       	0x00007fffb9accc4f _pthread_body + 176
14  libsystem_pthread.dylib       	0x00007fffb9accb9f _pthread_start + 274
15  libsystem_pthread.dylib       	0x00007fffb9acc3d5 thread_start + 13
Comment 1 Per Arne Vollan 2017-05-14 23:23:05 PDT
<rdar://problem/27446589>
Comment 2 Per Arne Vollan 2017-05-15 00:24:31 PDT
Created attachment 310115 [details]
Patch
Comment 3 Brent Fulgham 2017-05-15 10:18:14 PDT
Comment on attachment 310115 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=310115&action=review

> Source/WebCore/platform/graphics/avfoundation/AudioSourceProviderAVFObjC.mm:364
> +        return;

I wonder how we get into this state? We should probably ASSERT in "initCallback" if the passed 'tap' is nullptr, since I don't think this is supposed to happen.

It seems like this might happen if 'destroyMix()' is called, and then something attempts to use the AudioSourceProviderAVFObjC again (some queued 'process' callback gets invoked).
Comment 4 Per Arne Vollan 2017-05-16 02:56:24 PDT
Created attachment 310250 [details]
Patch
Comment 5 Per Arne Vollan 2017-05-16 03:00:12 PDT
(In reply to Brent Fulgham from comment #3)
> Comment on attachment 310115 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=310115&action=review
> 
> > Source/WebCore/platform/graphics/avfoundation/AudioSourceProviderAVFObjC.mm:364
> > +        return;
> 
> I wonder how we get into this state? We should probably ASSERT in
> "initCallback" if the passed 'tap' is nullptr, since I don't think this is
> supposed to happen.
> 
> It seems like this might happen if 'destroyMix()' is called, and then
> something attempts to use the AudioSourceProviderAVFObjC again (some queued
> 'process' callback gets invoked).

Thanks for reviewing! I have added an ASSERT in initCallback, and included a scenario of how this might happen in the changelog.
Comment 6 Per Arne Vollan 2017-05-16 03:03:24 PDT
Created attachment 310251 [details]
Patch
Comment 7 Brent Fulgham 2017-05-16 08:48:28 PDT
Comment on attachment 310251 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=310251&action=review

I think this looks fine, but I'd like to hear from Jer or Eric before landing.

> Source/WebCore/ChangeLog:19
> +        will crash.

Should we be asserting that the calls are being made on the main thread?
Comment 8 Jer Noble 2017-05-16 09:27:19 PDT
Comment on attachment 310251 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=310251&action=review

>> Source/WebCore/ChangeLog:19
>> +        will crash.
> 
> Should we be asserting that the calls are being made on the main thread?

If it's possible to destroy the tap simultaneous to using it on separate threads, then this code might reduce NULL-deref crashes, but might still lead to calling into destroyed objects.

> Source/WebCore/platform/graphics/avfoundation/AudioSourceProviderAVFObjC.mm:366
> +    if (!m_tap)
> +        return;
> +    

However, it should be possible to eliminate that possibility by doing something like:

RetainPtr<MTAudioProcessingTapRef> tap = m_tap;
if (!tap)
    return;

And then:

> Source/WebCore/platform/graphics/avfoundation/AudioSourceProviderAVFObjC.mm:369
>      OSStatus status = MTAudioProcessingTapGetSourceAudio(m_tap.get(), numberOfFrames, bufferListInOut, flagsOut, &rangeOut, &itemCount);

Using tap instead of m_tap in this call.

This is an unfortunate amount of ref-count churn, but the only other solution here would be to add a bunch of locks, which have their own performance problems.
Comment 9 Per Arne Vollan 2017-05-16 09:47:55 PDT
Created attachment 310267 [details]
Patch
Comment 10 Per Arne Vollan 2017-05-16 09:50:53 PDT
(In reply to Jer Noble from comment #8)
> Comment on attachment 310251 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=310251&action=review
> 
> >> Source/WebCore/ChangeLog:19
> >> +        will crash.
> > 
> > Should we be asserting that the calls are being made on the main thread?
> 
> If it's possible to destroy the tap simultaneous to using it on separate
> threads, then this code might reduce NULL-deref crashes, but might still
> lead to calling into destroyed objects.
> 
> > Source/WebCore/platform/graphics/avfoundation/AudioSourceProviderAVFObjC.mm:366
> > +    if (!m_tap)
> > +        return;
> > +    
> 
> However, it should be possible to eliminate that possibility by doing
> something like:
> 
> RetainPtr<MTAudioProcessingTapRef> tap = m_tap;

Do we still need locking to make sure this assignment is thread safe?

> if (!tap)
>     return;
> 
> And then:
> 
> > Source/WebCore/platform/graphics/avfoundation/AudioSourceProviderAVFObjC.mm:369
> >      OSStatus status = MTAudioProcessingTapGetSourceAudio(m_tap.get(), numberOfFrames, bufferListInOut, flagsOut, &rangeOut, &itemCount);
> 
> Using tap instead of m_tap in this call.
> 
> This is an unfortunate amount of ref-count churn, but the only other
> solution here would be to add a bunch of locks, which have their own
> performance problems.

Thanks for reviewing! I updated the patch.
Comment 11 Jer Noble 2017-05-16 14:54:34 PDT
View in context: https://bugs.webkit.org/attachment.cgi?id=310251&action=review

>>>> Source/WebCore/ChangeLog:19
>>>> +        will crash.
>>> 
>>> Should we be asserting that the calls are being made on the main thread?
>> 
>> If it's possible to destroy the tap simultaneous to using it on separate threads, then this code might reduce NULL-deref crashes, but might still lead to calling into destroyed objects.
> 
> Do we still need locking to make sure this assignment is thread safe?

I would file a follow-up bug to see if we need a blocking mechanism to ensure that the tap isn't freed while in the middle of a process(). We may need to add something.
Comment 12 Per Arne Vollan 2017-05-17 00:32:41 PDT
(In reply to Jer Noble from comment #11)
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=310251&action=review
> 
> >>>> Source/WebCore/ChangeLog:19
> >>>> +        will crash.
> >>> 
> >>> Should we be asserting that the calls are being made on the main thread?
> >> 
> >> If it's possible to destroy the tap simultaneous to using it on separate threads, then this code might reduce NULL-deref crashes, but might still lead to calling into destroyed objects.
> > 
> > Do we still need locking to make sure this assignment is thread safe?
> 
> I would file a follow-up bug to see if we need a blocking mechanism to
> ensure that the tap isn't freed while in the middle of a process(). We may
> need to add something.

I see that the MTAudioProcessingTapRef is passed in the processCallback() function. What do you think about just passing this on to the process() method, and use it there instead of using m_tap? This parameter will not be null even if the main thread sets m_tap to null, and I assume it is retained by MediaToolbox, so it can be used safely?
Comment 13 WebKit Commit Bot 2017-05-17 14:53:05 PDT
Comment on attachment 310267 [details]
Patch

Clearing flags on attachment: 310267

Committed r217005: <http://trac.webkit.org/changeset/217005>
Comment 14 WebKit Commit Bot 2017-05-17 14:53:07 PDT
All reviewed patches have been landed.  Closing bug.