Skip to content

Possible race on device disconnect, callback after close #953

@gavv

Description

@gavv

Android version(s): 9
Android device(s): Xiaomi Redmi Note 7
Oboe version: 1.4.2

Hi,

First of all, sorry if I'm reporting this to a wrong tracker. I'm still not 100% sure if this is Oboe bug, AAudio bug, or bug in my own code.

My app uses Oboe with BT SCO headset (AirPods) and creates two oboe streams (input and output). It also subscribes AudioManager events (from Java SDK), and when device disappears, it calls stream->stop(), then stream->close(), and then destroys stream; first for one stream, and then for another.

(I'm using shared stream with data and error callbacks).

At the same time, concurrently with the second stream close call, onAudioDeviceUpdate() callback is invoked somewhere in background. Then close() returns and I destroy the stream. And then AudioStreamLegacy segfaults in background. Before the segfault, clang UB sanitizer reports integrity check failure.

It seems to me that close() doesn't wait until onAudioDeviceUpdate() is finished, or onAudioDeviceUpdate() doesn't check that the stream is already closed, or it fails if the stream is being closed during its invocation, or it invokes something (error callback?) that is not being waited by close(), etc.

Here are the logs:

07-21 16:49:17.199  19797    19797                              MYAPP  I  stopping stream for device 35
07-21 16:49:17.199  19797    19797                             AAudio  D  AAudioStream_requestStop(0x6d948d2420)
07-21 16:49:17.200  20099    20130                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.200   2302     2958                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.202  19797    20238                        AudioRecord  I  AUDIO_INPUT_FLAG_FAST successful; frameCount 4096 -> 4096
07-21 16:49:17.212  20099    20130                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.212   2302     2958                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.228   2302     2958                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.228  20099    20130                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.229  19797    19822                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.437   2302     2958                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.437  19797    19822                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.438  20099    20130                       AudioManager  E  updatePortConfig port not found for handle: 31
07-21 16:49:17.439  19797    19797                              MYAPP  I  closing stream for device 35
07-21 16:49:17.439  19797    19797                             AAudio  D  AAudioStream_close(0x6d948d2420) called ---------------
07-21 16:49:17.439  19797    19863                  AudioStreamLegacy  D  onAudioDeviceUpdate() deviceId 14
07-21 16:49:17.439  19797    19797                       AAudioStream  D  destroying 0x6d948d2420, state = AAUDIO_STREAM_STATE_CLOSED
07-21 16:49:17.439  19797    19863                  AudioStreamLegacy  D  onAudioDeviceUpdate() DISCONNECT the stream now
07-21 16:49:17.439  19797    19797                             AAudio  D  AAudioStream_close(0x6d948d2420) returned 0 ---------
07-21 16:49:17.439  19797    19797                              MYAPP  I  stopping stream for device 31
07-21 16:49:17.439  19797    19797                             AAudio  D  AAudioStream_requestStop(0x6c948d4f80)
07-21 16:49:17.439  19797    19797                         AudioTrack  D  stop() called with 772032 frames delivered
07-21 16:49:17.441  19797    19797                              MYAPP  I  closing stream for device 31
07-21 16:49:17.441  19797    19797                             AAudio  D  AAudioStream_close(0x6c948d4f80) called ---------------
07-21 16:49:17.442  19792    19792                            wrap.sh  I  frameworks/av/media/libaaudio/src/legacy/AudioStreamLegacy.cpp:216:13: runtime error: control flow integrity check for type 'aaudio::AudioStreamLegacy' failed during non-virtual call (vtable address 0x005e8dbd52f0)
07-21 16:49:17.442  19792    19792                            wrap.sh  I  0x005e8dbd52f0: note: invalid vtable
07-21 16:49:17.442  19792    19792                            wrap.sh  I  00 00 00 00  58 de b9 8d 5e 00 00 00  40 cf b9 8d 5e 00 00 00  fc 9d 47 18 7f 00 00 00  34 4c bc 8d
07-21 16:49:17.442  19792    19792                            wrap.sh  I                ^ 
07-21 16:49:17.443  19797    19797                         AudioTrack  V  ~AudioTrack, releasing session id 25 from 19797 on behalf of 19797
07-21 16:49:17.446  19797    19797                       AAudioStream  D  destroying 0x6c948d4f80, state = AAUDIO_STREAM_STATE_CLOSED
07-21 16:49:17.446  19797    19797                             AAudio  D  AAudioStream_close(0x6c948d4f80) returned 0 ---------
07-21 16:49:17.446  19797    19797                              MYAPP  D  oboe stream destructor
07-21 16:49:17.446  19797    19797                              MYAPP  D  oboe stream destructor

Relevant lines are:

<<<< onAudioDeviceUpdate() is called in background >>>>

07-21 16:49:17.439  19797    19863                  AudioStreamLegacy  D  onAudioDeviceUpdate() DISCONNECT the stream now
07-21 16:49:17.439  19797    19797                             AAudio  D  AAudioStream_close(0x6d948d2420) returned 0 ---------

<<<< my app calls stop() >>>>

07-21 16:49:17.439  19797    19797                              MYAPP  I  stopping stream for device 31
07-21 16:49:17.439  19797    19797                             AAudio  D  AAudioStream_requestStop(0x6c948d4f80)
07-21 16:49:17.439  19797    19797                         AudioTrack  D  stop() called with 772032 frames delivered

<<<< my app calls close() >>>>

07-21 16:49:17.441  19797    19797                              MYAPP  I  closing stream for device 31
07-21 16:49:17.441  19797    19797                             AAudio  D  AAudioStream_close(0x6c948d4f80) called ---------------

<<<< sanitizer failure >>>>

07-21 16:49:17.442  19792    19792                            wrap.sh  I  frameworks/av/media/libaaudio/src/legacy/AudioStreamLegacy.cpp:216:13: runtime error: control flow integrity check for type 'aaudio::AudioStreamLegacy' failed during non-virtual call (vtable address 0x005e8dbd52f0)

And here is the backtrace:

 wrap.sh  I  SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior frameworks/av/media/libaaudio/src/legacy/AudioStreamLegacy.cpp:216:13 in
   DEBUG  F  *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
   DEBUG  F  Build fingerprint: 'xiaomi/lavender/lavender:9/PKQ1.180904.001/V11.0.6.0.PFGMIXM:user/release-keys'
   DEBUG  F  Revision: '0'
   DEBUG  F  ABI: 'arm64'
   DEBUG  F  pid: 19797, tid: 19863, name: Binder:19797_4  >>> tech.boring.boringaudio <<<
   DEBUG  F  signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
   DEBUG  F  x0  0000000000000000  x1  0000000000004d97  x2  0000000000000006  x3  0000000000000008
   DEBUG  F  x4  0000000000000000  x5  0000000000000000  x6  0000000000000000  x7  0000000000000000
   DEBUG  F  x8  0000000000000083  x9  727d373c9ee44947  x10 0000000000000000  x11 fffffffc7ffffbdf
   DEBUG  F  x12 0000000000000001  x13 0000005e76454270  x14 0000000000000000  x15 0000007f16a59b5c
   DEBUG  F  x16 0000007f19d942b0  x17 0000007f19cb3cd0  x18 0000000000000001  x19 0000000000004d55
   DEBUG  F  x20 0000000000004d97  x21 0000000820810411  x22 0000005e8dbd52e0  x23 0000005e76455588
   DEBUG  F  x24 0000007094916480  x25 0000007f168772c0  x26 0000007f168fc680  x27 0000007f168ee930
   DEBUG  F  x28 000000000000dd50  x29 0000005e76454ed0
   DEBUG  F  sp  0000005e76454e90  lr  0000007f19ca7144  pc  0000007f19ca716c
   DEBUG  F  backtrace:
   DEBUG  F  #00 pc 000000000002216c  /system/lib64/libc.so (abort+116)
   DEBUG  F  #01 pc 000000000005a6d8  /data/app/tech.boring.boringaudio-jDFB_zBuiZEygtRlTZq43g==/lib/arm64/libclang_rt.asan-aarch64-android.so (offset 0x50000)
   DEBUG  F  #02 pc 0000000000059670  /data/app/tech.boring.boringaudio-jDFB_zBuiZEygtRlTZq43g==/lib/arm64/libclang_rt.asan-aarch64-android.so (offset 0x50000)
   DEBUG  F  #03 pc 00000000000640c4  /data/app/tech.boring.boringaudio-jDFB_zBuiZEygtRlTZq43g==/lib/arm64/libclang_rt.asan-aarch64-android.so (offset 0x50000) (__ubsan_handle_cfi_check_fail_abort+128)
   DEBUG  F  #04 pc 000000000002606c  /system/lib64/libaaudio.so
   DEBUG  F  #05 pc 000000000002a574  /system/lib64/libaaudio.so (__cfi_check+5492)
   DEBUG  F  #06 pc 000000000002ee58  /system/lib64/libaaudio.so (aaudio::AudioStreamLegacy::onAudioDeviceUpdate(int)+856)
   DEBUG  F  #07 pc 000000000005d3e8  /system/lib64/libaudioclient.so (android::AudioRecord::onAudioDeviceUpdate(int, int)+296)
   DEBUG  F  #08 pc 0000000000064ffc  /system/lib64/libaudioclient.so (android::AudioSystem::AudioFlingerClient::ioConfigChanged(android::audio_io_config_event, android::sp<android::AudioIoDescriptor> const&)+3300)
   DEBUG  F  #09 pc 0000000000086140  /system/lib64/libaudioclient.so (android::BnAudioFlingerClient::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+360)
   DEBUG  F  #10 pc 000000000004fbf4  /system/lib64/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+136)
   DEBUG  F  #11 pc 000000000005d2a4  /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+520)
   DEBUG  F  #12 pc 000000000005cfe8  /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+156)
   DEBUG  F  #13 pc 000000000005d6d8  /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+108)
   DEBUG  F  #14 pc 000000000007fc5c  /system/lib64/libbinder.so (android::PoolThread::threadLoop()+24)
   DEBUG  F  #15 pc 00000000000100dc  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+284)
   DEBUG  F  #16 pc 00000000000b5a10  /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140)
   DEBUG  F  #17 pc 0000000000090328  /system/lib64/libc.so (__pthread_start(void*)+36)
   DEBUG  F  #18 pc 0000000000023a28  /system/lib64/libc.so (__start_thread+68)
 wrap.sh  I  Aborted
 wrap.sh  I  wrap.sh terminated by exit(134)

My own code looks sane to me: I just call close() and expect that it will wait everything that should be waited, and when it returns, I destroy the stream.

If my code is correct, I'm not sure whether it's related to Oboe or AAudio. Is there a chance that Oboe close() implementation is incomplete and doesn't wait for something?

Currently the reproducer is a rather large closed-source app. If my expectations to close() are correct and you think I'm using it right, and this may be related to Oboe, I can try to create a stand-alone reproducer. Please let me know if this makes sense.

Metadata

Metadata

Assignees

Labels

P1high prioritybug

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions