Bug Report: SIGSEGV in HostPlatformViewProps during Fabric ComponentDescriptor registration in brownfield apps with large DEX counts
Summary
Third-party Fabric codegen component descriptors (from react-native-screens, react-native-gesture-handler, react-native-safe-area-context) crash with SIGSEGV during ComponentDescriptorRegistry::add() when the brownfield AAR is embedded in a large Android application (43 DEX files). The same AAR works correctly in a small skeleton app (10 DEX files) with identical dependencies, native libraries, Kotlin version, and Android SDK versions.
Environment
- expo-brownfield: ~55.0.15
- Expo SDK: 55.0.8
- React Native: 0.83.2
- Android Gradle Plugin: 8.9.1
- Gradle: 8.11.1
- compileSdk/targetSdk: 36
- minSdk: 24
- Kotlin: 1.9.24 (host app), 2.1.20 (AAR build)
- New Architecture: enabled
- Devices tested: OnePlus CPH2423 (Android 15), Android 16 emulator (arm64)
Reproduction
Setup that WORKS (skeleton app)
A minimal Android app (com.yourapp.android) with:
- 10 DEX files
- Same brownfield AAR
- Same dependencies added (Twilio, Rive, Stripe, Firebase, ExoPlayer, Glide, ML Kit, Maps)
- Same jniLibs (OpenCV 18.7MB, SQLCipher, Scanner)
- Same Kotlin version (1.9.24)
react-native-screens + react-native-gesture-handler codegen in AAR
Result: RN screen renders with native-stack navigation header. No crash.
Setup that CRASHES (large app)
An existing production Android app (com.app.mNative) with:
- 43 DEX files (200+ activities, fragments, viewmodels)
- Identical brownfield AAR (same BuildIDs for all .so files)
- Same dependencies resolved from Maven
- Same Kotlin version, same AGP, same compileSdk
Result: SIGSEGV every time on any third-party codegen component.
Crash Details
Stack Trace (consistent across all crashes)
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x000000e397b9e420
backtrace:
#00 pc 00000000011ae534 libreactnative.so (HostPlatformViewProps::HostPlatformViewProps(...)+124)
#01 pc 00000000000280f0 libappmodules.so (RNGestureHandlerRootViewProps::RNGestureHandlerRootViewProps(...)+92)
#02 pc 00000000000250a8 libappmodules.so (void RawPropsParser::prepare<RNGestureHandlerRootViewProps>()+1120)
#03 pc 0000000000024bb4 libappmodules.so
#04 pc 0000000000024b08 libappmodules.so (concreteComponentDescriptorConstructor<...RNGestureHandlerRootView...>+36)
#05 pc 0000000000e1dd10 libreactnative.so (ComponentDescriptorRegistry::add(ComponentDescriptorProvider const&)+116)
#06 pc 0000000000e18fdc libreactnative.so (ComponentDescriptorProviderRegistry::createComponentDescriptorRegistry(...)+176)
...
#14 pc 0000000000f15a7c libreactnative.so (Scheduler::Scheduler(...)+1424)
...
#21 pc 0000000000ae5ae4 libreactnative.so (FabricUIManagerBinding::installFabricUIManager(...)+980)
Register State at Crash
x0 0000000000000000 (null)
x1 valid stack address
x2 0000000000000044 (68)
x3 ffffffffffffffff
BuildIDs (identical between working and crashing setups)
libreactnative.so: da38bb60ae28ed12d7c76d24ece13660372b5d28
libappmodules.so: 795418edb39f924b36e8a02b5f1af5b9b658db2f (gesture-handler only)
Key Observations
1. Core RN components work fine in the large app
Without any third-party codegen in the AAR (only libappmodules.so at 50KB with zero codegen), the large app renders React Native View, Text, ScrollView components successfully. Screenshot proof:
The "Hospital Management" dashboard screen rendered correctly with auth data, theme support, and back button handling.
2. Third-party codegen crashes regardless of which library
Tested with:
react-native-screens → SEGV in RNSScreenNavigationContainerProps
react-native-gesture-handler → SEGV in RNGestureHandlerRootViewProps
react-native-safe-area-context → SEGV in RNCSafeAreaViewProps
All crash at the same offset (+124) in HostPlatformViewProps::HostPlatformViewProps.
3. Systematic elimination of other causes
| Factor Tested |
Method |
Result |
| Dependencies (Twilio, Rive, Stripe, Firebase, etc.) |
Added all to skeleton app |
No crash |
| jniLibs (OpenCV 18.7MB, SQLCipher, Scanner) |
Added to skeleton app |
No crash |
| Kotlin version 1.9.24 |
Downgraded skeleton app |
No crash |
| DFM split APKs |
Moved to regular activities |
Still crashes |
| Android 15 physical device |
Tested on OnePlus |
Still crashes |
| Android 16 emulator |
Tested |
Still crashes |
| Removed all jniLibs from large app |
Tested |
Still crashes |
| Removed OpenCV specifically |
Tested |
Still crashes |
Separate process (:brownfield) |
Isolated from main process |
Still crashes |
| Empty Application.onCreate() |
Skipped all app init in brownfield process |
Still crashes |
4. The only remaining difference
|
Skeleton App (WORKS) |
Large App (CRASHES) |
| DEX file count |
10 |
43 |
| Java/Kotlin classes |
~500 |
~15,000+ |
| Application class |
Simple |
Complex (but crash persists even with empty onCreate) |
| Same brownfield AAR |
Yes |
Yes |
| Same .so BuildIDs |
Yes |
Yes |
| Same dependencies |
Yes (added all) |
Yes |
Root Cause Analysis
The registerComponentDescriptorsFromEntryPoint BSS variable
$ nm -D libreactnative.so | grep registerComponentDescriptorsFromEntryPoint
00000000012e11d0 B _ZN8facebook5react25DefaultComponentsRegistry42registerComponentDescriptorsFromEntryPointE
This is a BSS segment global variable (zero-initialized) in libreactnative.so. It's set during libreactnative.so's JNI_OnLoad and consumed by libappmodules.so's JNI_OnLoad.
The crash mechanism
libappmodules.so's JNI_OnLoad calls registerComponentDescriptorsFromEntryPoint to register third-party component descriptors
- Later,
FabricUIManagerBinding::installFabricUIManager() creates a Scheduler
- The
Scheduler constructor calls createComponentDescriptorRegistry() which iterates registered descriptors
- For each third-party descriptor, it calls
concreteComponentDescriptorConstructor<T>() → RawPropsParser::prepare<T>() → props constructor
- The props constructor (
RNGestureHandlerRootViewProps) calls base HostPlatformViewProps constructor
- At offset +124, the constructor dereferences a pointer computed from corrupted/uninitialized data → SEGV
Why core components don't crash
Core component descriptors (View, Text, ScrollView, Image) are registered inline in libreactnative.so's own initialization. Their ComponentDescriptorProviders are added in the same JNI_OnLoad context where the ComponentFactory is fully initialized. Third-party descriptors go through the separate libappmodules.so → registerComponentDescriptorsFromEntryPoint path.
Why DEX count matters
In a 43-DEX application, the ART runtime's class resolution path is different from a 10-DEX application. When libappmodules.so's JNI_OnLoad uses fbjni to resolve Java classes (like ComponentFactory, DefaultComponentsRegistry), the multi-DEX ClassLoader lookup may return classes that are partially initialized or from a different ClassLoader context. This corrupts the native ComponentDescriptorParameters (specifically the filterCallback std::function), leading to the SEGV when the props constructor tries to use it.
Related Issues
Workaround
Remove native platform directories from third-party RN libraries before expo prebuild:
rm -rf node_modules/react-native-screens/android node_modules/react-native-screens/ios
rm -rf node_modules/react-native-safe-area-context/android node_modules/react-native-safe-area-context/ios
rm -rf node_modules/react-native-gesture-handler/android node_modules/react-native-gesture-handler/ios
npx expo prebuild --clean
This removes native Fabric codegen while keeping JS functionality. Use @react-navigation/stack (JS-based animations) instead of @react-navigation/native-stack.
Expected Behavior
Third-party Fabric codegen component descriptors should register and initialize correctly regardless of the host app's DEX file count or class count.
Suggested Fix Areas
DefaultComponentsRegistry — Ensure the registerComponentDescriptorsFromEntryPoint callback captures a strong reference to the fully-initialized ComponentFactory native object, preventing stale pointer access
libappmodules.so JNI_OnLoad — Validate the entry point callback is non-null before invoking
ComponentDescriptorRegistry::add() — Null-check the filterCallback in ComponentDescriptorParameters before passing to props constructors
fbjni ClassLoader caching — Ensure the cached ClassLoader used during JNI_OnLoad is the correct one for multi-DEX applications
Bug Report: SIGSEGV in HostPlatformViewProps during Fabric ComponentDescriptor registration in brownfield apps with large DEX counts
Summary
Third-party Fabric codegen component descriptors (from
react-native-screens,react-native-gesture-handler,react-native-safe-area-context) crash with SIGSEGV duringComponentDescriptorRegistry::add()when the brownfield AAR is embedded in a large Android application (43 DEX files). The same AAR works correctly in a small skeleton app (10 DEX files) with identical dependencies, native libraries, Kotlin version, and Android SDK versions.Environment
Reproduction
Setup that WORKS (skeleton app)
A minimal Android app (
com.yourapp.android) with:react-native-screens+react-native-gesture-handlercodegen in AARResult: RN screen renders with native-stack navigation header. No crash.
Setup that CRASHES (large app)
An existing production Android app (
com.app.mNative) with:Result: SIGSEGV every time on any third-party codegen component.
Crash Details
Stack Trace (consistent across all crashes)
Register State at Crash
BuildIDs (identical between working and crashing setups)
libreactnative.so:da38bb60ae28ed12d7c76d24ece13660372b5d28libappmodules.so:795418edb39f924b36e8a02b5f1af5b9b658db2f(gesture-handler only)Key Observations
1. Core RN components work fine in the large app
Without any third-party codegen in the AAR (only
libappmodules.soat 50KB with zero codegen), the large app renders React NativeView,Text,ScrollViewcomponents successfully. Screenshot proof:The "Hospital Management" dashboard screen rendered correctly with auth data, theme support, and back button handling.
2. Third-party codegen crashes regardless of which library
Tested with:
react-native-screens→ SEGV inRNSScreenNavigationContainerPropsreact-native-gesture-handler→ SEGV inRNGestureHandlerRootViewPropsreact-native-safe-area-context→ SEGV inRNCSafeAreaViewPropsAll crash at the same offset (+124) in
HostPlatformViewProps::HostPlatformViewProps.3. Systematic elimination of other causes
:brownfield)4. The only remaining difference
Root Cause Analysis
The
registerComponentDescriptorsFromEntryPointBSS variableThis is a BSS segment global variable (zero-initialized) in
libreactnative.so. It's set duringlibreactnative.so'sJNI_OnLoadand consumed bylibappmodules.so'sJNI_OnLoad.The crash mechanism
libappmodules.so'sJNI_OnLoadcallsregisterComponentDescriptorsFromEntryPointto register third-party component descriptorsFabricUIManagerBinding::installFabricUIManager()creates aSchedulerSchedulerconstructor callscreateComponentDescriptorRegistry()which iterates registered descriptorsconcreteComponentDescriptorConstructor<T>()→RawPropsParser::prepare<T>()→ props constructorRNGestureHandlerRootViewProps) calls baseHostPlatformViewPropsconstructorWhy core components don't crash
Core component descriptors (View, Text, ScrollView, Image) are registered inline in
libreactnative.so's own initialization. TheirComponentDescriptorProviders are added in the sameJNI_OnLoadcontext where theComponentFactoryis fully initialized. Third-party descriptors go through the separatelibappmodules.so→registerComponentDescriptorsFromEntryPointpath.Why DEX count matters
In a 43-DEX application, the ART runtime's class resolution path is different from a 10-DEX application. When
libappmodules.so'sJNI_OnLoadusesfbjnito resolve Java classes (likeComponentFactory,DefaultComponentsRegistry), the multi-DEX ClassLoader lookup may return classes that are partially initialized or from a different ClassLoader context. This corrupts the nativeComponentDescriptorParameters(specifically thefilterCallbackstd::function), leading to the SEGV when the props constructor tries to use it.Related Issues
Workaround
Remove native platform directories from third-party RN libraries before
expo prebuild:This removes native Fabric codegen while keeping JS functionality. Use
@react-navigation/stack(JS-based animations) instead of@react-navigation/native-stack.Expected Behavior
Third-party Fabric codegen component descriptors should register and initialize correctly regardless of the host app's DEX file count or class count.
Suggested Fix Areas
DefaultComponentsRegistry— Ensure theregisterComponentDescriptorsFromEntryPointcallback captures a strong reference to the fully-initializedComponentFactorynative object, preventing stale pointer accesslibappmodules.soJNI_OnLoad— Validate the entry point callback is non-null before invokingComponentDescriptorRegistry::add()— Null-check thefilterCallbackinComponentDescriptorParametersbefore passing to props constructorsfbjniClassLoader caching — Ensure the cached ClassLoader used duringJNI_OnLoadis the correct one for multi-DEX applications