Fix Use After Free With Concurrent Java GC #1279

Closed
NickGerleman wants to merge 1 commits from export-D45556206 into main
NickGerleman commented 2023-05-03 20:46:49 -07:00 (Migrated from github.com)

Summary:
Fixes https://github.com/facebook/yoga/issues/1271

Java bindings for Yoga rely solely on garbage collection for memory management. Each Java YogaNode has references to its children and parent, along with an YGNodeRef which it owns. When the YogaNode is garbage collected, a finalizer is run to call YGNodeFree and free the underlying native Yoga Node.

This may cause a use-after-free if finalizers are run from multiple threads. This is because YGNodeFree does more than just freeing, but instead also interacts with its parent and children nodes to detach itself, and remove any dangling pointers. If multiple threads run finalizers at once, one may traverse and try to mutate a node which another is freeing.

This bit of behavior is counterintuitive from the API name anyway, so this diff makes a breaking change to YGNodeFree to instead mostly just be a free() (with a tracing step whose publishing should be thread safe). The existing behavior is exposed in an added function YGNodeDetachAndFree().

The majority of existing YGNodeFree usages will probably still work under the new behavior, but we don't have that many in fbsource (many instead call YGNodeFreeRecursive to free a whole tree at once). So for safety, I replaced existing usages of YGNodeFree outside of Yoga with YGNodeDetachAndFree to preserve existing behavior.

JavaScript and C# bindings also use YGNodeFree(), and both allow explicit freeing. For now I switched both of their behavior to detachment, to avoid exposing the ability to managed languages to corrupt memory.

Differential Revision: D45556206

Summary: Fixes https://github.com/facebook/yoga/issues/1271 Java bindings for Yoga rely solely on garbage collection for memory management. Each Java `YogaNode` has references to its children and parent, along with an `YGNodeRef` which it owns. When the `YogaNode` is garbage collected, a finalizer is run to call `YGNodeFree` and free the underlying native Yoga Node. This may cause a use-after-free if finalizers are run from multiple threads. This is because `YGNodeFree` does more than just freeing, but instead also interacts with its parent and children nodes to detach itself, and remove any dangling pointers. If multiple threads run finalizers at once, one may traverse and try to mutate a node which another is freeing. This bit of behavior is counterintuitive from the API name anyway, so this diff makes a breaking change to `YGNodeFree` to instead mostly just be a `free()` (with a tracing step whose publishing should be thread safe). The existing behavior is exposed in an added function `YGNodeDetachAndFree()`. The majority of existing `YGNodeFree` usages will probably still work under the new behavior, but we don't have that many in fbsource (many instead call `YGNodeFreeRecursive` to free a whole tree at once). So for safety, I replaced existing usages of `YGNodeFree` outside of Yoga with `YGNodeDetachAndFree` to preserve existing behavior. JavaScript and C# bindings also use `YGNodeFree()`, and both allow explicit freeing. For now I switched both of their behavior to detachment, to avoid exposing the ability to managed languages to corrupt memory. Differential Revision: D45556206
facebook-github-bot commented 2023-05-03 20:47:52 -07:00 (Migrated from github.com)

This pull request was exported from Phabricator. Differential Revision: D45556206

This pull request was **exported** from Phabricator. Differential Revision: [D45556206](https://www.internalfb.com/diff/D45556206)
facebook-github-bot commented 2023-05-03 22:17:38 -07:00 (Migrated from github.com)

This pull request was exported from Phabricator. Differential Revision: D45556206

This pull request was **exported** from Phabricator. Differential Revision: [D45556206](https://www.internalfb.com/diff/D45556206)
facebook-github-bot commented 2023-05-03 22:26:43 -07:00 (Migrated from github.com)

This pull request was exported from Phabricator. Differential Revision: D45556206

This pull request was **exported** from Phabricator. Differential Revision: [D45556206](https://www.internalfb.com/diff/D45556206)
facebook-github-bot commented 2023-05-10 01:44:10 -07:00 (Migrated from github.com)

This pull request was exported from Phabricator. Differential Revision: D45556206

This pull request was **exported** from Phabricator. Differential Revision: [D45556206](https://www.internalfb.com/diff/D45556206)
facebook-github-bot commented 2023-05-10 21:12:48 -07:00 (Migrated from github.com)

This pull request was exported from Phabricator. Differential Revision: D45556206

This pull request was **exported** from Phabricator. Differential Revision: [D45556206](https://www.internalfb.com/diff/D45556206)
facebook-github-bot commented 2023-05-10 22:55:59 -07:00 (Migrated from github.com)

This pull request has been merged in facebook/yoga@3b088c3383.

This pull request has been merged in facebook/yoga@3b088c3383e360126e46d7c4449aa7286a1a52e3.

Pull request closed

Sign in to join this conversation.
No description provided.