Fix HermesNode property access on released JSI objects#825
Fix HermesNode property access on released JSI objects#825brocollie08 merged 11 commits intomainfrom
Conversation
|
/canary |
2 similar comments
|
/canary |
|
/canary |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #825 +/- ##
===========================
===========================
☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
… throughout, instead of only in the beginning
51ce679 to
dbfd56a
Compare
…ctive (another race condition leading to cancellationException instead of runtime released)
| } | ||
| } | ||
| internal fun <T> Value.blockingLock(block: Value.() -> T): T = when { | ||
| context.runtime.lock.isHeldByCurrentThread -> block() |
There was a problem hiding this comment.
these shouldn't be impacted since they'll be on the same thread
| jsiObject.asValue(runtime) | ||
| }.let { format.decodeFromRuntimeValue(deserializer, it) } | ||
| format.decodeFromRuntimeValue(deserializer, jsiObject.asValue(runtime)) | ||
| } |
There was a problem hiding this comment.
i wrapped the whole thing here and above. mapUndefineToNull as well as decoding can both run into similar race conditions since they need to reach the c++ layer
There was a problem hiding this comment.
Don't those operations re-enter evaluateInJSThreadBlocking though? Would we be able to remove that? Or is keeping it in the same block just activating the shortcut?
There was a problem hiding this comment.
Do we need to make these updates for the other node impls?
There was a problem hiding this comment.
mapUndefinedToNull doesn't, decoding does, i moved decoding back out to minimize changes
sugarmanz
left a comment
There was a problem hiding this comment.
Looks fine — main concern is consistency across runtime impls. Even if functionally equivalent, the more it diverges, the more difficult it is to maintain.
| jsiObject.asValue(runtime) | ||
| }.let { format.decodeFromRuntimeValue(deserializer, it) } | ||
| format.decodeFromRuntimeValue(deserializer, jsiObject.asValue(runtime)) | ||
| } |
There was a problem hiding this comment.
Do we need to make these updates for the other node impls?
| // TODO: Put Dedicated Runtime Thread Context in the dispatcher context if we can? | ||
| return if (currentRuntimeThreadContext != null) { | ||
| block(currentRuntimeThreadContext) | ||
| ensureNotReleased { block(currentRuntimeThreadContext) } |
There was a problem hiding this comment.
Why do ensureNotReleased here? You're ignoring it for this case in graal:
https://github.com/player-ui/player/pull/825/changes#r3017129498
Just want to be consistent.
There was a problem hiding this comment.
yea you right, this shouldn't be necessary
| } | ||
|
|
||
| internal suspend fun <T> Runtime<*>.evaluateInJSThread(block: suspend RuntimeThreadContext.() -> T): T { | ||
| ensureNotReleased() |
There was a problem hiding this comment.
Should this whole function be wrapped in ensureNotReleased to avoid needing to call it multiple times?
| runBlocking { | ||
| evaluateInJSThread(runtime, block) | ||
| } | ||
| runBlocking { runtime.ensureNotReleased { evaluateInJSThread(runtime, block) } } |
There was a problem hiding this comment.
Do we need the check here if we're sure evaluateInJSThread will wrap block with it?
There was a problem hiding this comment.
it does, though evaluateInJSThread has the guard as well
This PR prevents a rare but high-impact Android crash in Hermes where the app can hit a native
NullPointerExceptionwhile reading JS backed objects. It does not change Player flow/state behavior; it only improves safety and diagnostics in a teardown race window.Context
The crash occurs when native code tries to read a property from a Hermes JSI object that has already been released (often due to timing/lifecycle during teardown or navigation).
This shows up as a fatal
NullPointerExceptioninside Hermes/JSI property access (getProperty), which crashes the app process.Testing
bazel test //jvm/hermes:test //jvm/core:testChange Type (required)
Indicate the type of change your pull request is:
patchminormajorN/ADoes your PR have any documentation updates?
Release Notes
Added a defensive guard in HermesNode so property reads on a released runtime/object fail safely by throwing a PlayerRuntimeException with context instead of a fatal native NPE.
Added regression tests:
📦 Published PR as canary version:
0.15.2--canary.825.33564Try this version out locally by upgrading relevant packages to 0.15.2--canary.825.33564