react-native
react-native copied to clipboard
App Crashes on Android 32bit When emit event from TurboModule
Description
Hello again everyone, @javache @cortinico.
This is continuation of this thread.
After testing the latest RC and nighly builds, crash appeared when emitting events from turbo modules on 32bit Android devices. The crash is always reproducible only on 32bit devices on signed production builds. I have created reproduction demo, which is basically the official demo code for creating turbo module with event:
note: nightly and v0.80-RC3 versions can be seen as PRs
Steps to reproduce
- Pull the repo, install the dependencies
- Run codegen on android
- Build signed apk. To create it you will need to create new demo key-store.
- To install the build apk in 32bit mode you can use
adb -s YOURDEVICE install --abi armeabi-v7a android/app/release/app-release.apk - Run the app, create key, save it. Than update the key and save it again. The app crashes when try to emit event from the turbo module.
React Native Version
0.80-rc3, nightly
Affected Platforms
Runtime - Android
Output of npx @react-native-community/cli info
System:
OS: macOS 15.5
CPU: (12) arm64 Apple M3 Pro
Memory: 120.23 MB / 36.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.12.0
path: /opt/homebrew/opt/nvm/versions/node/v22.12.0/bin/node
Yarn:
version: 1.22.22
path: /opt/homebrew/opt/nvm/versions/node/v22.12.0/bin/yarn
npm:
version: 10.9.0
path: /opt/homebrew/opt/nvm/versions/node/v22.12.0/bin/npm
Watchman:
version: 2024.12.02.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods: Not Found
SDKs:
iOS SDK:
Platforms:
- DriverKit 24.4
- iOS 18.4
- macOS 15.4
- tvOS 18.4
- visionOS 2.4
- watchOS 11.4
Android SDK:
API Levels:
- "28"
- "29"
- "30"
- "34"
- "35"
Build Tools:
- 34.0.0
- 35.0.0
System Images:
- android-28 | Google APIs ARM 64 v8a
- android-28 | Google ARM64-V8a Play ARM 64 v8a
- android-29 | Google APIs ARM 64 v8a
- android-29 | Google Play ARM 64 v8a
- android-30 | Google APIs ARM 64 v8a
- android-30 | Google Play ARM 64 v8a
- android-34 | Google APIs ARM 64 v8a
- android-34 | Google Play ARM 64 v8a
- android-35 | Google Play ARM 64 v8a
- android-35 | Pre-Release 16 KB Page Size Google Play ARM 64 v8a
- android-Baklava | Pre-Release 16 KB Page Size Google Play ARM 64 v8a
Android NDK: Not Found
IDEs:
Android Studio: 2024.3 AI-243.24978.46.2431.13363775
Xcode:
version: 16.3/16E140
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.12
path: /usr/bin/javac
Ruby:
version: 2.6.10
path: /usr/bin/ruby
npmPackages:
"@react-native-community/cli":
installed: 19.0.0
wanted: 19.0.0
react:
installed: 19.1.0
wanted: 19.1.0
react-native:
installed: 0.81.0-nightly-20250527-6c053006b
wanted: nightly
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: true
iOS:
hermesEnabled: true
newArchEnabled: true
Stacktrace or Logs
05-27 11:51:02.686 1426 2117 I ActivityManager: Killing 9327:com.android.keychain/1000 (adj 999): empty #21
05-27 11:51:02.687 1426 2117 I ActivityManager: Changes in 10254 5 to 19, 8 to 0
05-27 11:51:02.688 1426 1674 I UMR : B|Compact com.google.android.googlequicksearchbox:search(18732)
05-27 11:51:02.689 18732 18732 I cmvo : onStop
05-27 11:51:02.693 1426 1550 I libprocessgroup: Successfully killed process cgroup uid 1000 pid 9327 in 5ms
05-27 11:51:02.703 1426 1674 I UMR : E|Compact d_rss=1372KB
05-27 11:51:02.709 904 904 I Zygote : Process 9327 exited due to signal 9 (Killed)
05-27 11:51:02.833 11790 11790 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
05-27 11:51:02.833 11790 11790 F DEBUG : Build fingerprint: 'samsung/a32xeea/a32x:13/TP1A.220624.014/A326BXXSDCXJA:user/release-keys'
05-27 11:51:02.833 11790 11790 F DEBUG : Revision: '7'
05-27 11:51:02.833 11790 11790 F DEBUG : ABI: 'arm'
05-27 11:51:02.833 11790 11790 F DEBUG : Processor: '7'
05-27 11:51:02.833 11790 11790 F DEBUG : Timestamp: 2025-05-27 11:51:02.250465306+0300
05-27 11:51:02.833 11790 11790 F DEBUG : Process uptime: 1s
05-27 11:51:02.833 11790 11790 F DEBUG : Cmdline: com.awesomeproject
05-27 11:51:02.833 11790 11790 F DEBUG : pid: 11740, tid: 11779, name: mqt_v_js >>> com.awesomeproject <<<
05-27 11:51:02.833 11790 11790 F DEBUG : uid: 10521
05-27 11:51:02.833 11790 11790 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x00000004
05-27 11:51:02.833 11790 11790 F DEBUG : Cause: null pointer dereference
05-27 11:51:02.833 11790 11790 F DEBUG : r0 b7df5f00 r1 b7df5fa4 r2 00000004 r3 b7df5ff8
05-27 11:51:02.833 11790 11790 F DEBUG : r4 00000001 r5 e464cc9c r6 b7df5f10 r7 00000000
05-27 11:51:02.833 11790 11790 F DEBUG : r8 e4001609 r9 b7df5f9c r10 145fe958 r11 b7df5fa4
05-27 11:51:02.833 11790 11790 F DEBUG : ip 00000002 sp b7df5ee0 lr e1cca50b pc e1cc9a84
05-27 11:51:02.833 11790 11790 F DEBUG : backtrace:
05-27 11:51:02.833 11790 11790 F DEBUG : #00 pc 0052ba84 /apex/com.android.art/lib/libart.so (art::(anonymous namespace)::ArgArray::BuildArgArrayFromVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, art::ObjPtr<art::mirror::Object>, std::__va_list) (.__uniq.245181933781456475607640333933569312899)+164) (BuildId: bbc8525991a1fb6be508c71defdd34d8)
05-27 11:51:02.833 11790 11790 F DEBUG : #01 pc 0052c507 /apex/com.android.art/lib/libart.so (art::JValue art::InvokeVirtualOrInterfaceWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+386) (BuildId: bbc8525991a1fb6be508c71defdd34d8)
05-27 11:51:02.834 11790 11790 F DEBUG : #02 pc 003ded8d /apex/com.android.art/lib/libart.so (art::JNI<false>::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+444) (BuildId: bbc8525991a1fb6be508c71defdd34d8)
05-27 11:51:02.834 11790 11790 F DEBUG : #03 pc 00261129 /data/app/~~Rsv22hk04KoUaJ1tmZphlg==/com.awesomeproject-W_5OFIiJQo4tJbFAusGdfA==/base.apk!libreactnative.so (BuildId: aab956e17a170a7f)
05-27 11:51:02.834 11790 11790 F DEBUG : #04 pc 00333923 /data/app/~~Rsv22hk04KoUaJ1tmZphlg==/com.awesomeproject-W_5OFIiJQo4tJbFAusGdfA==/base.apk!libreactnative.so (facebook::react::JavaTurboModule::setEventEmitterCallback(facebook::jni::alias_ref<_jobject*>)+338) (BuildId: aab956e17a170a7f)
05-27 11:51:02.834 11790 11790 F DEBUG : #05 pc 0000b545 /data/app/~~Rsv22hk04KoUaJ1tmZphlg==/com.awesomeproject-W_5OFIiJQo4tJbFAusGdfA==/base.apk!libappmodules.so (facebook::react::NativeLocalStorageSpecJSI::NativeLocalStorageSpecJSI(facebook::react::JavaTurboModule::InitParams const&)+568) (BuildId: 1845c84c5d9dc31b8d30e3074a8418d7c586f3c5)
05-27 11:51:02.834 11790 11790 F DEBUG : #06 pc 0000ba05 /data/app/~~Rsv22hk04KoUaJ1tmZphlg==/com.awesomeproject-W_5OFIiJQo4tJbFAusGdfA==/base.apk!libappmodules.so (facebook::react::NativeLocalStorageSpec_ModuleProvider(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, facebook::react::JavaTurboModule::InitParams const&)+84) (BuildId: 1845c84c5d9dc31b8d30e3074a8418d7c586f3c5)
05-27 11:51:02.834 11790 11790 F DEBUG : #07 pc 0000fa03 /data/app/~~Rsv22hk04KoUaJ1tmZphlg==/com.awesomeproject-W_5OFIiJQo4tJbFAusGdfA==/base.apk!libappmodules.so (facebook::react::javaModuleProvider(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, facebook::react::JavaTurboModule::InitParams const&)+30) (BuildId: 1845c84c5d9dc31b8d30e3074a8418d7c586f3c5)
05-27 11:51:02.834 11790 11790 F DEBUG :
MANDATORY Reproducer
https://github.com/vladimirivanoviliev/rn079eventcrash
Screenshots and Videos
No response
It seems that on arm32 in packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java
this method
https://github.com/facebook/react-native/blob/e7901a720bcf3877e8a011f7b211c7d59c694853/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java#L139
is called with a null eventEmitterCallback which is assigned to mEventEmitterCallback in the following line. mEventEmitterCallback is then used to emit an event, but being null the app crashes.
Hmm, I wonder if caching jmethodId here is unsafe. The base implementation of this method is shared across all classes though.
Could you try replacing static jmethodID cachedMethodId = nullptr; with jmethodID cachedMethodId = nullptr;?
It seems that on arm32 in packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java
this method
Line 139 in e7901a7
protected void setEventEmitterCallback(CxxCallbackImpl eventEmitterCallback) { is called with a null eventEmitterCallback which is assigned to mEventEmitterCallback in the following line. mEventEmitterCallback is then used to emit an event, but being null the app crashes.
That would show up as a null pointer exception, which is not the error we're seeing.
I started getting the same SIGABRT crashes after upgrading to 0.79.1. It seems to affect many users, but I haven’t found any reliable solution yet.
@javache I tried removing the cached method id, but that didn't help. Investigating the code seems that the culpitt might be the use of CallVoidMethod variadic function, which is unsafe on 32bit. I tried using the CallVoidMethodA instead on my side and the crash is fixed:
void JavaTurboModule::configureEventEmitterCallback() {
JNIEnv* env = jni::Environment::current();
static jmethodID cachedMethodId = nullptr;
if (cachedMethodId == nullptr) {
jclass cls = env->GetObjectClass(instance_.get());
cachedMethodId = env->GetMethodID(
cls,
"setEventEmitterCallback",
"(Lcom/facebook/react/bridge/CxxCallbackImpl;)V");
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
}
auto callback = JCxxCallbackImpl::newObjectCxxArgs([&](folly::dynamic args) {
auto eventName = args.at(0).asString();
auto& eventEmitter = static_cast<AsyncEventEmitter<folly::dynamic>&>(
*eventEmitterMap_[eventName].get());
eventEmitter.emit(args.size() > 1 ? std::move(args).at(1) : nullptr);
});
jvalue args[1];
args[0].l = callback.release();
env->CallVoidMethodA(instance_.get(), cachedMethodId, args);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
}
@javache could you please verify that the fix is valid?
Investigating the code seems that the culpitt might be the use of CallVoidMethod variadic function, which is unsafe on 32bit.
Do you have any references on why that would be unsafe on 32-bit? Switching to CallVoidMethodA seems fine, but we'd need a comment to explain why we're doing so.
@javache From what I found, the variadic functions like CallVoidMethod are unsafe on 32bit due to not type checking the passed arguments at compile time. As far as I understand the 64bit cpus and ABIs are more forgiving with alignment and calling conventions. On 32bit the ABIs are strict as arguments are passed on the stack and if there is type/size/alignment issue it reads the wrong memory, which causes the SIGEGV crashes.
Edit: I also done extensive testing on my application with the above patch and everything seems stable now.
Hey @javache , @cortinico, Do you think it's possible this fix to be reviewed and included in the 0.80 as the Turbo modules events are still broken in that version?
@vladimirivanoviliev do you have a PR for this?
Hey everyone, @CaptainJeff, @javache, @cortinico, I just opened PR with the above change:
- https://github.com/facebook/react-native/pull/51695
Is there any news or date on when the Stable release for the 0.8? been encountering so many errors doing with the PR to just fix this issue :'(
@kenziy you can apply the patches manually as I already mentioned in the linked issue.
Thanks @vladimirivanoviliev I encountered multiple issue working on the PR and I am still new to react-native infact this is my first application, but I am wondering.. is this issue didn't exist on previous version. as I am considering to try to downgrade the version. if YES can you suggest on what version?
If this is your first application than most probably you are having different issues than the one we are discussing here. This issue is experienced only on 32bit android phones, and only when emitting events from custom turbo modules.
No, I am having the similar issue on a 32bit the Stack trace shows facebook::react::JavaTurboModule::setEventEmitterCallback(facebook,... and due to it the crash rating on my apps in google play is keep on increasing, at the moment I am reaching 3%.. but yeah thanks, probably I will retry your resolution while waiting for the actual stable release
Thank you
I'm also seeing this crash coming in a lot for my custom turbo module.
On RN 0.77.2
@sepperousseau I can confirm I'm facing the same issue with React Native 0.77.2, specifically on low-end Android devices. 52046
Also having this issue on 0.77.2, 0.79.3 and 0.79.4. Tried applying the patch from @vladimirivanoviliev with variants using instance_ and jinstance, but neither are working unfortunately. Does anyone have a fix for 0.77 or 0.79?
Tried applying the patch from @vladimirivanoviliev with variants using
instance_andjinstance, but neither are working unfortunately.
@mathieupost are you using build from source? As Android is prebuilt so applying patches will have no effects: https://reactnative.dev/contributing/how-to-build-from-source#update-your-project-to-build-from-source
Applying the patch and building from source resolved the issue for me! RN 0.79.4
Any chance this can be merged in a 0.79.5 release?
Closing as this has been fixed and will ship in 0.80.1
anyone tried with older versions? 0.76.9 ? shouldnt it be backported to at least >= 0.76 ?
Yes it needs backporting if at all possible as it's present on our 0.76 version - updating to 0.79+ just isn't possible in the time we've got to resolve the crash.
ended up disabling new arch for android.... building out the source with the fix created lots of other problems... build problems etc
ended up disabling new arch for android.... building out the source with the fix created lots of other problems... build problems etc
What types of issues did you find after trying the build from source change?
ended up disabling new arch for android.... building out the source with the fix created lots of other problems... build problems etc
What types of issues did you find after trying the build from source change?
mostly random timeouts and out of memory issues, building both locally and on circle, and dont wanna change to whole build setup......