fix: Fix: EXC_BAD_ACCESS in RCTInlineMessageNative.updateProps on iOS Fabric#581
Conversation
Sample app builds 📱Below you will find the list of the latest versions of the sample apps. It's recommended to always download the latest builds of the sample apps to accurately test the pull request.
|
hollyschilling-cio
left a comment
There was a problem hiding this comment.
My C++ is pretty rusty, but the Objective C portion looks good and this should fix the bug referenced.
| NSAssert(self.bridge != nil, @"Bridge is nil when %@", context); | ||
| } | ||
|
|
||
| - (instancetype)init { |
There was a problem hiding this comment.
Non-blocking: would it be worth keeping -init and funneling it into -initWithFrame:CGRectZero (or extracting a shared setup helper)? I think the current change is correct, but making the _props and bridge setup explicit on every init path might remove reliance on UIKit’s init -> initWithFrame: forwarding.
There was a problem hiding this comment.
Better safe than sorry 😂 .. added!
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
## [6.4.1](6.4.0...6.4.1) (2026-04-23) ### Bug Fixes * Fix: EXC_BAD_ACCESS in RCTInlineMessageNative.updateProps on iOS Fabric ([#581](#581)) ([e93e8df](e93e8df))
Problem
Users on iOS with React Native New Architecture (Fabric) hit EXC_BAD_ACCESS (KERN_INVALID_ADDRESS) inside -[RCTInlineMessageNative updateProps:oldProps:] when InlineInAppMessageView is mounted. 13 users reported
it in production, see this report. Stack:
Hypotheses
H1 — oldProps parameter can be nullptr (the reporter's suggestion).
On Insert mutations, Fabric calls
updateProps:props oldProps:nullptr ([RCTMountingManager.mm](https://gitlab.123xe.vn/opensource/react-native/-/blob/7f0a4f72b445f45a317ccff9a90cfe53ffa4493e/React/Fabric/Mounting/RCTMountingManager.mm#L69)). Any subclass dereferencing the oldProps parameter without a null guard would crash on first mount.That's not likely the problem because our updateProps: implementation did not touch the oldProps parameter; it casted the
_propsivar instead. A null guard on _props would always pass, as _props is never null at this lifecycle stage.H2 — _props has the wrong runtime type on first mount (our diagnosis, most likely).
In the original init override, we never installed a concrete default for
_props. During [super init], the chain reachesRCTViewComponentView.initWithFrame:, which assigns:_props = ViewShadowNode::defaultSharedProps();This is a std::shared_ptr, not
InlineMessageNativeProps. On the first updateProps: call, static_pointer_cast(_props) reinterprets that pointer. oldViewProps.elementId then attempts to read a std::string 24 bytes past the end of the ViewProps allocation. If those bytes happen to form a malformed std::string with a garbage data pointer, the subsequent std::string::operator!= forwards to _platform_memcmp on invalid memory.Reproduction attempt
Was unable to do reproduce 🔴
Built a local test screen that mounts InlineInAppMessageView under a changing React key to force repeated fresh allocations. The crash did not reproduce.
Solution
1. Install a concrete _props default
Replaced the init override with initWithFrame: (the UIView designated initializer) and assigned the concrete default before creating the Swift bridge:
From the first updateProps: onward, _props has the correct runtime type, the static_pointer_cast is well-defined, and RCTViewComponentView's debug RCTAssert at updateProps: is satisfied.
2. Diff against the oldProps parameter with a null guard
Rewrote updateProps: to cast the oldProps parameter (the value Fabric passes us) rather than the _props ivar. When Fabric calls with oldProps == nullptr (first mount), the override applies all props unconditionally; on subsequent calls it diffs oldViewProps.elementId against the new value exactly as before.
Why this fix?
_props = <Subclass>ShadowNode::defaultSharedProps()insideinitWithFrame: is the canonical Fabric component-view initializer. Every first-party RN Fabric component view follows it. In the repo bundled with thisexample app (React/Fabric/Mounting/ComponentViews/)
The same base class enforces the invariant in
RCTViewComponentView.mm:217-223, asserting in debug builds that subclass _props is not the plain ViewProps — i.e. exactly the rule the previous init override was violating.Does Android have the same problem?
Not affected, Android's
InlineInAppMessageViewManageris aSimpleViewManagerwith per-prop @ReactProp setters (setElementId(view, value)). There is no C++ Props struct, no _props ivar, and no updateProps(oldProps, newProps) hook — so the class of bug described in H2 is structurally impossible on the Android side.Note
Medium Risk
Touches iOS Fabric component-view initialization and
updatePropsdiffing logic; while aimed at preventing a production crash, it could affect prop updates/lifecycle behavior for inline in-app messages. Lockfile-only dependency bumps are low risk but may alter local build/tooling behavior.Overview
Fixes a crash in the iOS Fabric inline message component by switching
RCTInlineMessageNativeto initialize viainitWithFrame:and setting_propstoInlineMessageNativeShadowNode::defaultSharedProps()before the firstupdateProps.Updates
updatePropsto not cast/diff against the_propsivar; it now null-guardsoldProps(which can benullptron first mount) and applieselementIdon first mount, diffing only on subsequent updates. Also refreshes the example app’s Ruby/Node lockfiles with minor dependency updates (e.g.,fastlane,react-native/metro, lint/tooling packages).Reviewed by Cursor Bugbot for commit 632706c. Bugbot is set up for automated code reviews on this repo. Configure here.