Skip to content

FilterAudioStream use-after-free when releasing shared_ptr before error callback #2325

@jg-hot

Description

@jg-hot

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

  1. Build and run the modified minimal-oboe sample contained in the FilterAudioStreamUAF-repro branch
  2. Open the app on a physical device with AAudio (Android 9+)
  3. Leave the "Use FilterAudioStream" toggle checked (default)
  4. Tap "Start Audio"
  5. Plug in or unplug headphones
  6. After ~2 seconds, ASAN reports a use-after-free in logcat (run the included symbolicate.py if needed)
  7. 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 forceConversion parameter is added to openStream() to ensure FilterAudioStream is used
  • The sample disables automatic stream restart in onErrorAfterClose()
  • Instead, the app explicitly resets its shared_ptr to the stream after closing it in response to ACTION_HEADSET_PLUG
  • The debug delay in oboe_aaudio_error_thread_proc_common is enabled to ensure the shared_ptr is 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 = 

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions