Summary
PR #259 ("Fix weak reference creation", merged 2025-11-07) fixed intermittent AVs during Node-API weak-reference creation for HostObject/proxy-flagged objects. The subsequent upstream merge PR #264 ("Merge static_h branch from upstream Hermes repo", merged 2026-02-24) silently removed all of #259's changes from API/hermes_node_api/hermes_node_api.cpp, and they are still absent from main today. We are hitting the regressed crash in production.
Is that revert intentional because the issue was supposed to be fixed on the new code?
Evidence the fix was reverted
API/hermes_node_api/hermes_node_api.cpp, presence of #259's identifiers (needsFinalizerTracking, initializeStorage, and the isHostObject() || isProxyObject() rejection in getExternalPropertyValue):
| Commit |
Date |
#259 code present? |
d7ca19b3 — #259 merge |
2025-11-07 |
yes (introduced) |
bc3d0ed7 — #260 FB sync |
2025-11-19 |
yes |
4df97b14 — #264 static_h merge |
2026-02-24 |
NO — all three removed |
4d4c2836 — #280 sync |
2026-03-10 |
no |
94aa5e1d — #311 (our shipped build) |
2026-04-21 |
no |
main (checked 2026-07-06) |
— |
no |
At 94aa5e1d, getExternalPropertyValue (lines 3660-3685) is back to the pre-#259 shape: getPredefinedProperty(...) then defineOwnProperty(...) with no HostObject/JSProxy rejection, and NodeApiReference has no needsFinalizerTracking()/initializeStorage().
The crash we observe after upgrading to RNW 0.81.27 (regressed build 0.0.0-2604.21001-94aa5e1d, win32-x86-release)
EXCEPTION_ACCESS_VIOLATION_READ @ 0x00000020 on the RN JS thread, intermittent, correlates with memory pressure (32-bit process near VA exhaustion; Hermes heap ~44 MB, 97.6% full, ~669 collections).
- Repro workload: heavy
UIManager.createNode churn; each node creation wraps the instance handle in a jsi::WeakObject:
UIManager.createNode (UIManagerBinding.cpp:216)
→ InstanceHandle ctor — jsi::WeakObject(runtime, instanceHandle) (InstanceHandle.cpp:17)
→ NodeApiJsiRuntime::createWeakObject (:1830)
→ NodeApiRefCountedPointerValue::createNodeApiRef (:2260)
→ napi_create_reference → hermes.dll → AV
It looks like similar entry path as the stack in #259's description.
If it can be helpful:
Crash site (static disassembly of the shipped DLL)
Faulting instruction hermes.dll +0x22a59b, inside the function at +0x22a4b0, which is hermes::vm::detail::findTrap (JSProxy.cpp) with detail::slots() inlined:
// lib/VM/JSProxy.cpp:24 at 94aa5e1d
ProxySlots &slots(JSObject *self) {
if (auto *proxy = dyn_vmcast<JSProxy>(self)) // CellKind 0x41 → slots_ @ +0x14
return proxy->slots_;
auto *cproxy = dyn_vmcast<JSCallableProxy>(self); // CellKind 0x48 → slots_ @ +0x20
assert(cproxy && "JSProxy methods must be passed JSProxy or JSCallableProxy");
return cproxy->slots_; // release: assert gone → &(((JSCallableProxy*)0)->slots_) == 0x20
}
Compiled x86 (verified in the shipped DLL):
+0x22a569: movzx edi, byte ptr [edx+3] ; CellKind
+0x22a56d: shl edi, 0x18
+0x22a570: cmp edi, 0x41000000 ; JSProxy?
+0x22a576: mov ebx, 0
+0x22a57b: cmove ebx, edx
+0x22a57e: cmp edi, 0x48000000 ; JSCallableProxy?
+0x22a584: cmove esi, edx ; esi was zeroed
+0x22a58c: add esi, 0x20 ; nullptr->slots_ == 0x20 if neither kind
+0x22a591: lea edx, [ebx+0x14]
+0x22a598: cmovne esi, edx
+0x22a59b: mov edx, [esi] ; <<< AV: reads 0x20
Fault registers match exactly: esi=0x20, edx=0x14, edi=0xffff0000.
So a JSProxy trap lookup is reached for an object that is neither JSProxy nor JSCallableProxy — the class of misroute #259's isProxyObject()/isHostObject() rejection in getExternalPropertyValue guarded against, during weak-reference finalizer-tracking setup.
Happy to provide the full symbolicated trace and disassembly notes.
Summary
PR #259 ("Fix weak reference creation", merged 2025-11-07) fixed intermittent AVs during Node-API weak-reference creation for
HostObject/proxy-flagged objects. The subsequent upstream merge PR #264 ("Mergestatic_hbranch from upstream Hermes repo", merged 2026-02-24) silently removed all of #259's changes fromAPI/hermes_node_api/hermes_node_api.cpp, and they are still absent frommaintoday. We are hitting the regressed crash in production.Is that revert intentional because the issue was supposed to be fixed on the new code?
Evidence the fix was reverted
API/hermes_node_api/hermes_node_api.cpp, presence of #259's identifiers (needsFinalizerTracking,initializeStorage, and theisHostObject() || isProxyObject()rejection ingetExternalPropertyValue):d7ca19b3— #259 mergebc3d0ed7— #260 FB sync4df97b14— #264 static_h merge4d4c2836— #280 sync94aa5e1d— #311 (our shipped build)main(checked 2026-07-06)At
94aa5e1d,getExternalPropertyValue(lines 3660-3685) is back to the pre-#259 shape:getPredefinedProperty(...)thendefineOwnProperty(...)with no HostObject/JSProxy rejection, andNodeApiReferencehas noneedsFinalizerTracking()/initializeStorage().The crash we observe after upgrading to RNW 0.81.27 (regressed build
0.0.0-2604.21001-94aa5e1d, win32-x86-release)EXCEPTION_ACCESS_VIOLATION_READ @ 0x00000020on the RN JS thread, intermittent, correlates with memory pressure (32-bit process near VA exhaustion; Hermes heap ~44 MB, 97.6% full, ~669 collections).UIManager.createNodechurn; each node creation wraps the instance handle in ajsi::WeakObject:It looks like similar entry path as the stack in #259's description.
If it can be helpful:
Crash site (static disassembly of the shipped DLL)
Faulting instruction
hermes.dll +0x22a59b, inside the function at+0x22a4b0, which ishermes::vm::detail::findTrap(JSProxy.cpp) withdetail::slots()inlined:Compiled x86 (verified in the shipped DLL):
Fault registers match exactly:
esi=0x20,edx=0x14,edi=0xffff0000.So a JSProxy trap lookup is reached for an object that is neither
JSProxynorJSCallableProxy— the class of misroute #259'sisProxyObject()/isHostObject()rejection ingetExternalPropertyValueguarded against, during weak-reference finalizer-tracking setup.Happy to provide the full symbolicated trace and disassembly notes.