-
Notifications
You must be signed in to change notification settings - Fork 615
Description
Android version(s): Android 8-16
Android device(s): Samsung Galaxy A03s, A05, A16, A06. Xiaomi Redmi 14C, Redmi Note 8 Pro, Tecno Spark 7, many others (any device with AAudio)
Oboe version: 1.10.0
App name used for testing: Modified minimal-oboe sample in FilterAudioStreamUAF-repro (forked repository linked below)
Source:
https://github.com/jg-hot/oboe/tree/FilterAudioStreamUAF-repro
Compare 1.10.0 <- FilterAudioStreamUAF-repro:
1.10.0...jg-hot:oboe:FilterAudioStreamUAF-repro
Short description
Using a FilterAudioStream in combination with AudioStreamBuilder::openStream(shared_ptr) causes a use-after-free crash (ASAN) or SIGSEGV (production) when the app releases its shared_ptr to the FilterAudioStream before oboe_aaudio_error_thread_proc_common runs to completion.
The error occurs because the call to oboeStream->lockWeakThis() in AudioStreamAAudio::internalErrorCallback() does not safely retain the parent FilterAudioStream object, which is then dereferenced as the AudioStreamErrorCallback in oboe_aaudio_error_thread_proc_common due to the callbacks being swapped here.
Notably, this bug only occurs when using FilterAudioStream. The call to lockWeakThis() does safely retain the AudioStreamAAudio object itself. When FilterAudioStream is not used, the crash does not occur, as demonstrated by the sample.
This bug appears to be the root cause of #2130.
Steps to reproduce
- Build and run the modified
minimal-oboesample contained in the FilterAudioStreamUAF-repro branch - Open the app on a physical device with AAudio (Android 9+)
- Leave the "Use FilterAudioStream" toggle checked (default)
- Tap "Start Audio"
- Plug in or unplug headphones
- After ~2 seconds, ASAN reports a use-after-free in logcat (run the included
symbolicate.pyif needed) - To confirm the issue is specific to
FilterAudioStream, uncheck "Use FilterAudioStream" and repeat steps 4–5; the crash does not occur
Additional setup in the sample that exposes the bug:
- A
forceConversionparameter is added toopenStream()to ensureFilterAudioStreamis used - The sample disables automatic stream restart in
onErrorAfterClose() - Instead, the app explicitly resets its
shared_ptrto the stream after closing it in response toACTION_HEADSET_PLUG - The debug delay in
oboe_aaudio_error_thread_proc_commonis enabled to ensure theshared_ptris released before dereferencing the error callback
Expected behavior
This behavior should match that of a regular AudioStream without a FilterAudioStream wrapper, which does not crash in the same scenario.
Actual behavior
Use-after-free crash occurs when FilterAudioStream is used and the app releases its shared_ptr during headset plug/unplug.
Production SIGSEGV
The stack trace below occurs, on average, once per ~2.2K daily active users in a production app. The first write access to FilterAudioStream in AudioStream::updateFramesWritten() causes a SIGSEGV.
Crashed: Thread: SIGSEGV 0x0000007981b62100
#00 pc 0x13a100 libc++_shared.so (__unw_getcontext) (BuildId: b04675a35ad96f8a9dcaa073e3bd31d4536f00ad)
#01 pc 0x2c490 liboboe.so (oboe::AudioStream::close() [AudioStream.cpp:44]) (BuildId: 2e902f2cb969e05f46a756fbe567733d71bfb03e)
#02 pc 0x2ef04 liboboe.so (non-virtual thunk to oboe::FilterAudioStream::onErrorAfterClose(oboe::AudioStream*, oboe::Result) [FilterAudioStream.h:200]) (BuildId: 2e902f2cb969e05f46a756fbe567733d71bfb03e)
#03 pc 0x2b7bc liboboe.so (void* std::__ndk1::__thread_proxy[abi:ne180000]<std::__ndk1::tuple<std::__ndk1::unique_ptr<std::__ndk1::__thread_struct, std::__ndk1::default_delete<std::__ndk1::__thread_struct> >, void (*)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result), std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result> >(void*) [invoke.h:344]) (BuildId: 2e902f2cb969e05f46a756fbe567733d71bfb03e)
#04 pc 0xb3358 libc.so (BuildId: 91887fc17756b96752d59d99fca49b04)
#05 pc 0x516c8 libc.so (BuildId: 91887fc17756b96752d59d99fca49b04)
Note: the production app uses Oboe version 1.9.0. See below for the exact lines causing the crash:
https://github.com/google/oboe/blob/1.9.0/src/common/AudioStream.cpp#L44
https://github.com/google/oboe/blob/1.9.0/src/common/FilterAudioStream.h#L200
ASAN output from sample:
The sample app crashes earlier via ASAN on the line below (the first read access to the callback). The root cause (freed FilterAudioStream memory) is the same in both cases.
https://github.com/google/oboe/blob/1.10.0/src/aaudio/AudioStreamAAudio.cpp#L87
Abort message: '=================================================================
==25152==ERROR: AddressSanitizer: heap-use-after-free on address 0x0045a4e98210 at pc 0x0070bc318d14 bp 0x0070c4e2ead0 sp 0x0070c4e2eac8
READ of size 8 at 0x0045a4e98210 thread T29
oboe_aaudio_error_thread_proc_common(oboe::AudioStreamAAudio*, oboe::Result)
/home/dev/oboe/src/aaudio/AudioStreamAAudio.cpp:87:42
oboe_aaudio_error_thread_proc_shared(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result)
/home/dev/oboe/src/aaudio/AudioStreamAAudio.cpp:112:5
decltype(std::declval<void (*)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result)>()(std::declval<std::__ndk1::shared_ptr<oboe::AudioStream>>(), std::declval<oboe::Result>())) std::__ndk1::__invoke[abi:ne180000]<void (*)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result), std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result>(void (*&&)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result), std::__ndk1::shared_ptr<oboe::AudioStream>&&, oboe::Result&&)
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__type_traits/invoke.h:344:25
void std::__ndk1::__thread_execute[abi:ne180000]<std::__ndk1::unique_ptr<std::__ndk1::__thread_struct, std::__ndk1::default_delete<std::__ndk1::__thread_struct>>, void (*)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result), std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result, 2ul, 3ul>(std::__ndk1::tuple<std::__ndk1::unique_ptr<std::__ndk1::__thread_struct, std::__ndk1::default_delete<std::__ndk1::__thread_struct>>, void (*)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result), std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result>&, std::__ndk1::__tuple_indices<2ul, 3ul>)
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__thread/thread.h:190:3
void* std::__ndk1::__thread_proxy[abi:ne180000]<std::__ndk1::tuple<std::__ndk1::unique_ptr<std::__ndk1::__thread_struct, std::__ndk1::default_delete<std::__ndk1::__thread_struct>>, void (*)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result), std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result>>(void*)
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__thread/thread.h:199:3
asan_thread_start(void*)
out/lib/compiler-rt-aarch64-ndk-cxx/out/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:239:28
#3 0x71c87cbf50 (/apex/com.android.runtime/lib64/bionic/libc.so+0xfcf50) (BuildId: 35a04626779868e5f2a30e171716818d) [No symbols available]
#4 0x71c875e500 (/apex/com.android.runtime/lib64/bionic/libc.so+0x8f500) (BuildId: 35a04626779868e5f2a30e171716818d) [No symbols available]
0x0045a4e98210 is located 368 bytes inside of 416-byte region [0x0045a4e980a0,0x0045a4e98240)
freed by thread T25 (DefaultDispatch) here:
operator delete(void*)
out/lib/compiler-rt-aarch64-ndk-cxx/out/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:143:3
std::__ndk1::__shared_count::__release_shared[abi:ne180000]()
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__memory/shared_ptr.h:154:7
std::__ndk1::__shared_weak_count::__release_shared[abi:ne180000]()
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__memory/shared_ptr.h:183:25
std::__ndk1::shared_ptr<oboe::AudioStream>::~shared_ptr[abi:ne180000]()
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__memory/shared_ptr.h:645:17
std::__ndk1::shared_ptr<oboe::AudioStream>::reset[abi:ne180000]()
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__memory/shared_ptr.h:695:50
SimpleNoiseMaker::release()
/home/dev/oboe/samples/minimaloboe/src/main/cpp/SimpleNoiseMaker.cpp:100:13
Java_com_example_minimaloboe_AudioPlayer_stopAndReleaseAudioStreamNative
/home/dev/oboe/samples/minimaloboe/src/main/cpp/MinimalOboeJNI.cpp:61:13
#7 0x713ff7ef70 (/apex/com.android.art/lib64/libart.so+0x37ef70) (BuildId: 4ccb65ae9ac5ad5da3af5a342d5b0b92) [No symbols available]
previously allocated by thread T25 (DefaultDispatch) here:
operator new(unsigned long)
out/lib/compiler-rt-aarch64-ndk-cxx/out/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:86:3
oboe::AudioStreamBuilder::openStreamInternal(oboe::AudioStream**, bool)
/home/dev/oboe/src/common/AudioStreamBuilder.cpp:165:47
oboe::AudioStreamBuilder::openStream(std::__ndk1::shared_ptr<oboe::AudioStream>&, bool)
/home/dev/oboe/src/common/AudioStreamBuilder.cpp:243:19
SimpleNoiseMaker::open(bool)
/home/dev/oboe/samples/minimaloboe/src/main/cpp/SimpleNoiseMaker.cpp:40:15
Java_com_example_minimaloboe_AudioPlayer_startAudioStreamNative
/home/dev/oboe/samples/minimaloboe/src/main/cpp/MinimalOboeJNI.cpp:45:29
#5 0x713ff7ef70 (/apex/com.android.art/lib64/libart.so+0x37ef70) (BuildId: 4ccb65ae9ac5ad5da3af5a342d5b0b92) [No symbols available]
Thread T29 created by T27 (AudioTrack) here:
___interceptor_pthread_create
out/lib/compiler-rt-aarch64-ndk-cxx/out/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:250:3
std::__ndk1::__libcpp_thread_create[abi:ne180000](long*, void* (*)(void*), void*)
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__threading_support:317:10
std::__ndk1::thread::thread<void (&)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result), std::__ndk1::shared_ptr<oboe::AudioStream>&, oboe::Result&, void>(void (&)(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result), std::__ndk1::shared_ptr<oboe::AudioStream>&, oboe::Result&)
/opt/dev/android-sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/c++/v1/__thread/thread.h:209:14
oboe::AudioStreamAAudio::internalErrorCallback(AAudioStreamStruct*, void*, int)
/home/dev/oboe/src/aaudio/AudioStreamAAudio.cpp:189:21
#3 0x7138778e88 (/system/lib64/libaaudio_internal.so+0x28e88) (BuildId: 29e37125e739e7eee69fc0b8299a7df0) [No symbols available]
#4 0x713877be14 (/system/lib64/libaaudio_internal.so+0x2be14) (BuildId: 29e37125e739e7eee69fc0b8299a7df0) [No symbols available]
#5 0x71c859d440 (/system/lib64/libaudioclient.so+0x9c440) (BuildId: 2d191cdeeae11b23f29d33757d8aff17) [No symbols available]
#6 0x71c859c5a0 (/system/lib64/libaudioclient.so+0x9b5a0) (BuildId: 2d191cdeeae11b23f29d33757d8aff17) [No symbols available]
#7 0x71dc46e550 (/system/lib64/libutils.so+0x13550) (BuildId: 62128a51f31d41bbd0d14333ccf95e95) [No symbols available]
#8 0x71c56807bc (/system/lib64/libandroid_runtime.so+0xcd7bc) (BuildId: b5d5146e5e759c3c9bd976d55711685a) [No symbols available]
asan_thread_start(void*)
out/lib/compiler-rt-aarch64-ndk-cxx/out/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:239:28
#10 0x71c875e500 (/apex/com.android.runtime/lib64/bionic/libc.so+0x8f500) (BuildId: 35a04626779868e5f2a30e171716818d) [No symbols available]
Thread T27 (AudioTrack) created by T25 (DefaultDispatch) here:
___interceptor_pthread_create
out/lib/compiler-rt-aarch64-ndk-cxx/out/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:250:3
#1 0x71dc46ddcc (/system/lib64/libutils.so+0x12dcc) (BuildId: 62128a51f31d41bbd0d14333ccf95e95) [No symbols available]
#2 0x71c568089c (/system/lib64/libandroid_runtime.so+0xcd89c) (BuildId: b5d5146e5e759c3c9bd976d55711685a) [No symbols available]
#3 0x71dc46e2e4 (/system/lib64/libutils.so+0x132e4) (BuildId: 62128a51f31d41bbd0d14333ccf95e95) [No symbols available]
#4 0x71c8596aa4 (/system/lib64/libaudioclient.so+0x95aa4) (BuildId: 2d191cdeeae11b23f29d33757d8aff17) [No symbols available]
#5 0x7138782f0c (/system/lib64/libaaudio_internal.so+0x32f0c) (BuildId: 29e37125e739e7eee69fc0b8299a7df0) [No symbols available]
#6 0x7138779790 (/system/lib64/libaaudio_internal.so+0x29790) (BuildId: 29e37125e739e7eee69fc0b8299a7df0) [No symbols available]
#7 0x71386f7e44 (/system/lib64/libaaudio.so+0x4e44) (BuildId: c932d9af346b357ee5f6f6b11f70d48c) [No symbols available]
oboe::AudioStreamAAudio::open()
/home/dev/oboe/src/aaudio/AudioStreamAAudio.cpp:420:38
oboe::AudioStreamBuilder::openStreamInternal(oboe::AudioStream**, bool)
/home/dev/oboe/src/common/AudioStreamBuilder.cpp:196:23
oboe::AudioStreamBuilder::openStreamInternal(oboe::AudioStream**, bool)
/home/dev/oboe/src/common/AudioStreamBuilder.cpp:137:31
oboe::AudioStreamBuilder::openStream(std::__ndk1::shared_ptr<oboe::AudioStream>&, bool)
/home/dev/oboe/src/common/AudioStreamBuilder.cpp:243:19
SimpleNoiseMaker::open(bool)
/home/dev/oboe/samples/minimaloboe/src/main/cpp/SimpleNoiseMaker.cpp:40:15
Java_com_example_minimaloboe_AudioPlayer_startAudioStreamNative
/home/dev/oboe/samples/minimaloboe/src/main/cpp/MinimalOboeJNI.cpp:45:29
#14 0x713ff7ef70 (/apex/com.android.art/lib64/libart.so+0x37ef70) (BuildId: 4ccb65ae9ac5ad5da3af5a342d5b0b92) [No symbols available]
Thread T25 (DefaultDispatch) created by T0 (ple.minimaloboe) here:
___interceptor_pthread_create
out/lib/compiler-rt-aarch64-ndk-cxx/out/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:250:3
#1 0x714032f054 (/apex/com.android.art/lib64/libart.so+0x72f054) (BuildId: 4ccb65ae9ac5ad5da3af5a342d5b0b92) [No symbols available]
#2 0x713ff7ef70 (/apex/com.android.art/lib64/libart.so+0x37ef70) (BuildId: 4ccb65ae9ac5ad5da3af5a342d5b0b92) [No symbols available]
#3 0x15bc73a7fffffffd (<unknown module>)
oboe_aaudio_error_thread_proc_common(oboe::AudioStreamAAudio*, oboe::Result)
/home/dev/oboe/src/aaudio/AudioStreamAAudio.cpp:87:42
oboe_aaudio_error_thread_proc_shared(std::__ndk1::shared_ptr<oboe::AudioStream>, oboe::Result)
/home/dev/oboe/src/aaudio/AudioStreamAAudio.cpp:112:5
Device
This bug should reproduce on any device using AAudio. The device below was used for testing:
ro.product.brand = samsung
ro.product.manufacturer = samsung
ro.product.model = SM-S135DL
ro.product.device = a03su
ro.product.cpu.abi = arm64-v8a
ro.build.description = a03sutfnssu-user 13 TP1A.220624.014 S135DLUDS8AYI1 release-keys
ro.hardware = mt6765
ro.hardware.chipname =
ro.arch =