iOS 12 Safari Array reverse bug
stackoverflow.com
stackoverflow.com
I find it quite worrying that devs already wrote a lib to fix the issue but didn't fill a bug report to Apple and shared it on SO. Anyway, a trendy HN post might be enough to get the attention of some Apple devs and I've pinged a dev just in case: https://twitter.com/ArmandGrillet/status/1042339847384518656
It is worrying... but as someone who has filed my fair share of bug reports to all the major browser projects it's also understandable. Filling good bug reports takes effort, especially for the more obscure ones to produce small and reliable test cases. In my experience only the largest projects have the man power to acknowledge let alone address all bug reports. I abandoned reporting bugs to IE, Edge and Safari long ago because this effort often goes to waste.
I get it though, browsers are one of the largest most complex incoherent pieces of software everyone uses today, but that's also why I think only fully open source (well engaged) projects work well in this area.
As an aside: This is the same reason I think the browser needs to get simple again (without loosing capability) - RE James Mickens thoughts on this. Then browser diversity and man power becomes a moot point.
You can spend a lot of time writing a good bug report for Apple; it's annoying that they don't bother to respond at all a lot of the time.
The people who answered it explain that it's a bug, thereby answering the question, and expand as to why they think it's a bug. One person figured out a patch. The bug was already filed and fixed nearly a month ago but doesn't seem to have been released yet to the current versions of Safari.
If there's already a bug filed and marked fixed but you can see that it isn't fixed yet for the people using Safari, and it matters to you, why would sharing it be worrisome?
You could argue that the person who answered with a patch didn't know the bug was already filed with Apple, and so they should have sought out how to file it before sharing it. But I think it ignores how intimidating it can be for someone to file an official bug report with a hugely popular piece of software maintained by one of the largest companies in the world. It's even more intimidating if you've done it before and the report you filed ended up being wrong (many maintainers are not very considerate when responding to mistaken bug reports, which can wreak havoc on a developer's self esteem).
Meanwhile you have platforms like StackOverflow and Hacker News which vastly lower the bar in terms of needing to be right, since they operate more as conversation facilitators than official registers of legitimate bugs. It's much easier to share what you think you've found with a group of your peers, than it is to go on record telling a group of experts that they've made a mistake in the thing that they're experts on.
I don't know if the outcome is worrisome or not, but it definitely seems expected to me... even inevitable.
It's the same reason you talk with your friends about how annoyed you are with potholes not being filled before you take your concerns to a town hall meeting. It's a way to refine your thoughts in a low-stakes settings with peers before formalizing an issue to run up the ladder.
I also just checked and you need an Apple-ID to report a defect in Safari. Which I for one don't have.
The only bug tracker I use that doesn't have this barrier is the Debian one.
And that's if you can even find the right place to report this kind of thing. Some in this thread are pointing to the webkit bugtracker which I've never used, but I have used and had issues closed as dupes in the apple bugtracker with no real info other than it's a dupe of another link I can't view...
It's not impossible, but it's a lot more time than I could see having to spend on something like this right now.
And I don't think it's fair to criticize someone for not wanting to put that work in, especially if they found a workaround and already posted it somewhere publicly viewable. I'd much rather get a well written bug report from someone that has the time and cares rather than 10 half-assed reports from people who just want to make one so they don't get yelled at or to view the status of another bug.
Update: the bug has already been filed in both the WebKit bug tracker and Radar, so no need for further reports. https://bugs.webkit.org/show_bug.cgi?id=188794
I completely agree, which is why I was just pointing out that the "it only takes a few minutes" is technically true, but a little misleading as nobody is going to be able to write a good bug report, create an apple account, report the bug, have it most likely get closed as a dupe, request access to the dupe, get access to the dupe, subscribe to the issue and wait for it to be fixed and released in "a few minutes".
Whereas making a polyfill could realistically happen in 30 minutes or so, and publishing it to NPM if you feel like it maybe another 10 if you are familiar with the process. And I don't feel like people should be criticized for not wanting to jump through Apple's hoops to report a bug and have it get fixed upstream when the alternative is not only faster, easier, and more public, but also can get deployed to your users right now.
Speaking of that: I do actually maybe have an Apple-ID account because a clueless person with a name similar to mine used one of my Email-Addresses to sign up with Apple.
Needless to say the bug made it to the final release of iOS 12. I’m done filling out bug reports for this reason.
Hmm, they must get so many duplicates...
Bug reports sometimes also contain proprietary code that help Apple pinpoint the bug. If you worked for a company and were working on a brand new software product, would you want your competitors to have the ability to search bug reports for your email address (or all addresses at *@xyz.com), knowing that you are having trouble with a specific API?
BTW, Apple Developer Relations has updated its privacy policy for bug reports where data you submit will be deleted from inactive bug reports after a period of time.
The submitter can check "This report is security sensitive or has confidential information that should not be shared further than necessary to fix the bug."
Most projects have this in the form of a default-public bug tracker, and private emails to the developer or security@ mailing list.
It's not difficult to have both private and public bugs where the submitter chooses which is appropriate.
The submitter doesn't always know when they are submitting sensitive or confidential information. And that's not going to be fixed any time soon or with any simple solution. There are system diagnostics reports that can be many megabytes and contain potentially confidential information, and it would be entirely unreasonable to expect a user, even a developer user, to know how to trawl through all of these reports with a level of expertise to discern every possible case of leakage.
And that's just the personal / private information... which, by the way, Apple from what I've seen seems to strive to keep OUT of the diagnostic reports, but that doesn't mean they are perfect at this. And that doesn't even begin to address the information that might be, totally unknown to the submitter, exploitable in other ways.
As far as what other projects have, macOS is a highly complex operating system along with a tightly integrated ecosystem of apps and services, which is designed to, and relied on to, "just work"... while most other systems are either much smaller, or are not maintaining a level of smooth operation and security that can match the "it just works" level of reliability standard that macOS aims for. So maybe the bug reporting systems of all those other projects are appropriate for those other projects, but that doesn't make those systems and their practices appropriate for macOS.
Those countless open source projects aren't up to the same standard as macOS in terms of a combination of complexity plus the expectation of "it just works"... note I said the combination of these two things, not just one of them at a time.
Why should anybody work for a megacorp for free?
So to me the unwillingness to help Apple with this is not a strange response. Especially considering Apple made Safari closed-source.
The bug report doesn't fix the problem right away. The shim fixes the problem right away.
Reporting the bug will cause a fix for everybody eventually. Adding the shim will fix it for me.
If we want to be nice to Apple - and get rid of the shim eventually - sure, we write a bug report. But if we can fix it before reporting, that gets priority.
Array.prototype.reverse modifies JSImmutableButterfly
You have to wonder how an immutable butterfly can be modified?!A couple of examples of why “immutable” types are actually mutable:
* string interning - a string may technically be immutable, but identical strings can be coalesced by the runtime. * immutable objects are not generally immortal: the runtime will eventually want to free the data, which means it must be mutable by the runtime.
Basically at the runtime level if you forget to check all the correct flags you can easily do the wrong thing. If you look at the bugzilla report you can see the comment from the apple engineer about needing to fix this particular foot gun.
See e.g. http://phrack.org/papers/attacking_javascript_engines.html section 1.2
jv v = jv_array(); // ref count == 1
v = jv_array_append(v, jv_number(0)); // Ostensibly copy
v = jv_array_append(v, jv_number(1)); // but actually mutate
Here, in all cases there is exactly one reference to the array `v`, so `jv_array_append()` actually modifies it in place. Do note that `jv_array_append()` returns a new `jv` value though, and that this is necessary to support the copy-on-write case: jv v = jv_array(); // ref count == 1
jv v2 = jv_copy(v); // ref count == 2 now!
v = jv_array_append(v, jv_number(0)); // Copy!
// Now `v`'s ref count == 1 again (and so does `v2`'s!)
v = jv_array_append(v, jv_number(1)); // Ostensibly copy, actually mutate
You can imagine similar mechanics in an ECMAScript implementation.Note that in jq itself this is mostly not something a user ever has to think about because one is always passing modified structures to an expression on the right:
(.foo = "bar") | ... # this sees .foo == "bar"
but it can be user-visible anyways: ((.foo = "bar") | ...), # ... sees .foo == "bar"
.foo # but here .foo is as it was before
Here the last reference to `.foo` happens outside the lexical context of the assignment to `.foo`, so the assignment is not visible. Under the covers this all is made possible by the C jv API's copy-on-write design where functions always consume a reference to their input `jv` values (except `jv_copy()`, `jv_get_kind()`, `jv_string_value()`, and `jv_number_value()`) and return a new `jv` value to replace the one that was "modified".Hopefully this gets this (HN) thread seen by the right people and messages flying in the right directions internally :)
However JavaScript has no similar notion of constant data but implementations try to infer it as an optimisation. In this case that inference was wrong.
I think the memory structure looks something like:
arr -> box1 -> 1,2,3,4
Reversing: arr -> box1 -> 4,3,2,1
But the data occupying the same region of memory before and after the sort. So then when the page is refreshed the JavaScript doesn’t change and the literal data must be included in the “parsed/compiled” form that is reused and so it is initialised as arr -> box2 -> 4,3,2,1
The correct behaviour should be as follows: 1. arr -> box1 -> loc1: 1,2,3,4
2. Reverse
3. arr -> box1 -> loc2: 4,3,2,1
4. Refresh
5. arr -> box2 -> loc1: 1,2,3,4
The “box” corresponds to the JavaScript object for the array (so mutating the array data can change the box, the data it points at, but not just the reference “arr” as the object might be pointed to from elsewhere). And this box has another pointer to the data for the array (and presumably some bit flag to say whether that is copy-on-write or not). This allows for more efficient array functions when the data doesn’t actually changeThe cachable code is kept in a big function source->bytecode hash table.
The cacheable bytecode is then “linked” to a bytecode that is optimized for execution speed. That bytecode has things like create_array, etc that can take the immutable backing store object we generated earlier.
This means that if you have multiple functions with identical source code you only have to do the expensive parse+compile step once, and you end up with multiple independent functions using the same constant/immutable backing stores. This saves memory and helps performance.
Unfortunately it adds complexity - now the mutable is objects have “immutable” backing stores, so you have to implement copy-on-write semantics in order to not share state between different instances of the linked code. In this case it appears that a required CoW check was missing :(
There was a serious WebAssembly regression in iOS 11.2 that makes wasm effectively useless [1]. Devs had to disable wasm and wait for months until iOS 11.3 is released.
Apple never releases a iOS hot fix just for a single Safari bug. Then why Apple don't let users to update Safari separately?
Fixing this bug requires shipping new versions of system frameworks, which is pretty much the definition of an OS update.
Or, they could bundle a separate version of the WebKit frameworks just for Safari (like the Safari Technical Preview does) – but then loading a website in your app would use a different stack of frameworks than loading that site in Safari, and no one wants that either.
everyday we stray further from god
(function() {
function buggy() {
function detect() {
var a = [0, 1];
a.reverse();
return a[0] === 0;
}
return detect() || detect();
}
if(!buggy()) return;
Array.prototype._reverse = Array.prototype.reverse;
Array.prototype.reverse = function reverse() {
if (Array.isArray(this)) this.length = this.length;
return Array.prototype._reverse.call(this);
}
var nonenum = {enumerable: false};
Object.defineProperties(Array.prototype, {
_reverse: nonenum,
reverse: nonenum,
});
})();
Look at how elaborate the repo is by comparison:The rest is just tests / npm config / license
You answered your own question. Especially in the JS world
Checking arbitrary complex interactions isn’t trivial so more complex classes of bugs can only get testing when they’re discovered.
However, this bug sounds pretty serious! :-S