Skip to content

App Crashes on Android 32bit When emit event from TurboModule #51628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
vladimirivanoviliev opened this issue May 27, 2025 · 9 comments
Open
Labels
Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. Platform: Android Android applications.

Comments

@vladimirivanoviliev
Copy link

vladimirivanoviliev commented May 27, 2025

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

  1. Pull the repo, install the dependencies
  2. Run codegen on android
  3. Build signed apk. To create it you will need to create new demo key-store.
  4. To install the build apk in 32bit mode you can use adb -s YOURDEVICE install --abi armeabi-v7a android/app/release/app-release.apk
  5. 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

@lcarrettin
Copy link

It seems that on arm32 in packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java

this method

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.

@javache
Copy link
Member

javache commented May 27, 2025

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

react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BaseJavaModule.java

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.

@burakakyol
Copy link

burakakyol commented May 27, 2025

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.

Image

@vladimirivanoviliev
Copy link
Author

@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?

@javache
Copy link
Member

javache commented May 28, 2025

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.

@migueldaipre migueldaipre added Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. and removed Needs: Triage 🔍 labels May 28, 2025
@vladimirivanoviliev
Copy link
Author

vladimirivanoviliev commented May 28, 2025

@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.

@vladimirivanoviliev
Copy link
Author

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?

@CaptainJeff
Copy link

@vladimirivanoviliev do you have a PR for this?

@vladimirivanoviliev
Copy link
Author

Hey everyone, @CaptainJeff, @javache, @cortinico, I just opened PR with the above change:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. Platform: Android Android applications.
Projects
None yet
Development

No branches or pull requests

7 participants