-
Notifications
You must be signed in to change notification settings - Fork 24.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix use of uninitialized memory when unsetting flex
property
#12245
Conversation
99428d1
to
6a28ded
Compare
When a property on a node is set for the first time, we read and store the property's initial value before updating it. Later, if the property is nullified in JavaScript, we'll write the earlier-obtained initial value to the native representation. In order to obtain the initial value, there must be a property getter that corresponds to the setter. If no getter exists, the slot to store the initial value will remain silently uninitialized. If the property is nullified from JavaScript, we'll write the garbage value of that uninitialized variable into the native representation, and chaos will ensue. This commit makes four changes to resolve this issue: 1. It adds an assertion that a getter exists every time a property is written. It would be better to enforce this statically, but I'm not aware of a means to accomplish this. 2. It adds a getter for the `flex` property, which previously lacked one. Prior to this change, setting then removing the `flex` property on a view would result in layout glitches and redboxes about `NaN` and `Infinity` in the bound and offset of a view. 3. It adds a getter for the `on` property for RCTSwitch, which can never be undefined, but is triggering the newly-added assertion. 4. It adds an `unselectedItemTintColor` property for RCTTabBar, which appears to have been an oversight. There have been a number of complaints about transient redboxes with messages about illegal `NaN`s and `Infinity`s. Because this bug pertains to uninitialized memory which non-deterministic by nature, it's possible that a number of these difficult-to-repro issues will be resolved by this bugfix.
6a28ded
to
6fd9fe7
Compare
Thanks for fixing this! |
@javache has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
Thanks @javache. I realized from the failing tests that my solution isn't quite acceptable. It requires that we discover all asymmetrical properties through the triggering of a runtime assertion, which isn't a sustainable approach. (By asymmetrical, I mean properties that do not follow the Some (but not all) of the boolean properties in the standard iOS libraries shirk the convention, opting for an At this point, I think it's useful to step back and revisit the challenge underpinning this bug. The challenge is that we have no consistent way to signal a JavaScript The original attempt to solve this problem (in 62177db) used a heuristic approach, opting to consider the initial value of the property to be its In an ideal world, we could mandate that all native props exposed to JavaScript be boxed types, but that ship has clearly sailed. To further improve the heuristic approach (while also avoiding a noisy assertion), I propose we do the following:
I'd like to hear your thoughts. If you think the combined approach above is enough to lay this issue to rest, I'll code it up and update the branch. If you think we should treat asymmetrical properties as errors, we'll need to announce this change as breaking (and suss out whether there are any further asymmetrical properties in the core |
Actually, the simpler option here may be to use |
And I think it would be fine for this to be a breaking change. I haven't checked the internal tests so I'm not sure how much breakage this will cause, but perhaps to limit the impact of the breakage we could only enforce the assert for primitive type and default to nil for everything that's an object? |
I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification. |
Except that we use this macro for all getting all sorts of types, so it doesn't play nicely with primitives. |
Regarding
According to the docs, If we make this a breaking change, it's worth noting that it will impact almost all third-party modules, much in the way that 0.40 basically forced everybody to rev their modules. Are you really sure the RN team would be on board with that? |
A simpler way to just avoid a crash here is to extend the |
Summary: Helps mitigate part of #12245 while we wait for a more comprehensive solution. Reviewed By: emilsjolander Differential Revision: D4571776 fbshipit-source-id: 185cd1b0d3af37724136a37471df412c2000dfe4
This PR appears to be abandoned. Closing for now. |
This PR fixes a bug that can be reproduced on iOS 9.3 with v0.41.1 using this simple app. It's important to note that the bug pertains to uninitialized memory and is therefore not guaranteed to reproduce at all.
Description of the issue and fix
When a property on a node is set for the first time, we read and store the property's initial value before updating it. Later, if the property is nullified in JavaScript, we'll write the earlier-obtained initial value to the native representation.
In order to obtain the initial value, there must be a property getter that corresponds to the setter. If no getter exists, the slot to store the initial value will remain silently uninitialized. If the property is
nullified from JavaScript, we'll write the garbage value of that uninitialized variable into the native representation, and chaos will ensue.
This commit makes three changes to resolve this issue:
It adds an assertion that a getter exists every time a property is written. It would be better to enforce this statically, but I'm not aware of a means to accomplish this.
It adds a getter for the
flex
property, which previously lacked one. Prior to this change, setting then removing theflex
property on a view would result in layout glitches and redboxes aboutNaN
andInfinity
in the bound and offset of a view.It adds a getter for the
on
property for RCTSwitch, which can never be undefined, but is triggering the newly-added assertion.There have been a number of complaints (#2616, #3406, to name a few) about transient redboxes with messages about illegal
NaN
s andInfinity
s. Because this bug pertains to uninitialized memory which non-deterministic by nature, it's possible that a number of these difficult-to-repro issues will be resolved by this bugfix.Test plan
Use the simple repro app to confirm that the issue has been resolved.
Concerns
I'm not in love with the idea of adding a dynamic assertion for asymmetrical (setter-only) properties. I would appreciate any suggestions of alternatives.
I discovered this issue when I was trying to figure out why
react-native-foldview
was repeatedly redboxing on iOS 9.3. It looks like other people have reported this, too. (cc: @jmurzy)I believe that this regression pertains to commit 62177db by @nicklockwood (reviewed by @javache), so it would be great to have either of your eyes on it.