I read the entire thing, and honestly the heap grooming is very interesting, but really that's the boring part -- lots of trial and error, padding memory, etc. Also interesting that linked-lists aren't used by Apple† (and Ian Beer's suggestion that they ought to use them), but that's neither here nor there. Getting kernel memory read/write is also very interesting, albeit (again) a bit tedious. At the end of the day, it all started with this:
> Using two MacOS laptops and enabling AirDrop on both of them I used a kernel debugger to edit the SyncTree TLV sent by one of the laptops, which caused the other one to kernel panic due to an out-of-bounds memmove.
How did this even pass the _smell_ test? How did it get through code reviews and auditing? You're allocating from an untrusted source. It's like memory management 101. I mean, my goodness, it's from a wireless source, at that.
† In this specific scenario, namely the list of `IO80211AWDLPeer`s.
Because attackers only have to find one place that was unlucky in implementation, and hence defenders are burdened with eliminting every last one of them.
This is why implementing your network protocols in unsafe languages is bad. Testing can just find some bugs, not ensure absence of bugs.
Now I know it's deeply comforting to think if you just had "safety" you could write all the code you want with abandon and the computer would tell you if you did it wrong, but this is a sophomoric attitude that you will either abandon when you have the right experiences, or you will go into management where the abject truth in this statement will be used to keep programmer salaries in the gutter, and piss-poor managers in a job. Meanwhile, these "safe" languages will give you nothing but shadows you'll mistake for your own limitations.
My suggestion is just learn how to write secure code in C. It's an unknown-unknown for you at the moment, so you're going to have to learn how to tackle that sort of thing, but the good news is that (with the right strategy) many unknown-unknowns can be attacked using the same tricks. That means if you do learn how to write secure code in C, then the skills you develop will be transferable to other languages and other domains, and if you still like management, those skills will even be useful there.
You can’t just build a better developer when a single mistake is end game. Even if you do everything right you can still run into problems.
The reason for that is because large projects can’t have only one developer. As soon as you have multiple developers you have a problem. What happens when two developers begin working on the same base commit. Developer A makes a change to remove a contractual behavior that is not relied upon. Developer B makes a change that relies on this contractual behavior. Both changes are correct on their own, could very well pass code review simultaneously, and then both merge without conflicts. And then your last life line is whatever guarantees you have via static analysis, etc. (notably, this could still fail in a memory safe language if there aren’t any safe guards for this particular logic bug. Nothing is a panacea. Having more tools to write safer code, though, can at least help prevent some of these cases.)
That’s assuming everyone is perfect and has unlimited time to write perfectly sound code always. And it still fails.
You point to Rust but nobody said it had to be Rust. Still, just because Rust is not a panacea does not mean it has no value. On the contrary, while there has been decades to hone practices for secure C, Rust is a relative newcomer and obviously shows a ton of promise. It and other new memory safe languages are very likely to take a bite out of C usages where security is important. You can embrace this or deny it... but if you think it’s not happening, you should definitely take a look at the writing on the wall, because it’s certainly there. On the other hand, there are also other approaches. I believe seL4 is doing C code with proofs of correct operation. (Admittedly, I do not fully understand what guarantees this gives you and how, but it sounds promising based on descriptions. There could still be bugs in the proofs, but it certainly raises the bar.)
Doctors benefit from better tools and processes too, but this is all wildly beside the point, because my point was not that we can’t build a better programmer, it’s that we can’t just build a better programmer, for the reason that I then went on to outline.
You bring up a great point, though. Historically, C is often not trusted for software where people’s lives are on the line. Thus bringing up doctors is a great example of how building a better programmer is not good enough. There’s an entire class of “safety critical” programming practices and standards and it was common to prefer a language like Ada that made more bugs and logic errors into compiler errors.
> it’s that we can’t just build a better programmer, for the reason that I then went on to outline.
Apologies for missing your point. I thought you meant by this:
> The reason for that is because large projects can’t have only one developer.
... that you meant we would have to change more things in our business and software cultures rather than just making programmers smart (something I could agree with), not that you believed this was some kind of truism.
I don’t believe this is true. Why are you convinced it is impossible to do “big” things without big teams of idiots?
> Historically, C is often not trusted for software where people’s lives are on the line. Thus bringing up doctors is a great example of how building a better programmer is not good enough.
I don’t see how one of these things has to do with the other. Can you explain the link?
> My suggestion is just learn how to write secure code in C.
That is a good suggestion to an individual developer. What is your suggestion to a lead developer of a big organisation? Let’s say to the CTO of Apple.
You can see at that level of abstraction the “make sure every one of your developers know how to write secure code in C and they never slip up” manifestly doesnt work.
You can fault individuals for bugs up to a certain point, but if we want to make secure systems we have to change how we are making them. To make the whole process resistant to oopsies.
I don't think this is a case of "both sides have a point" when it comes to blaming individuals for vulnerabilities or suggesting they learn to write secure C. We're way past these things.
> What is your suggestion to a lead developer of a big organisation? Let’s say to the CTO of Apple.
He can pay for my advice if he really wants to hear it, but this isn't really about programming at this point because we're talking about business goals which can have all sorts of priorities besides making quality software.
Do we want software quality? Then we want better programmers.
> if we want to make secure systems we have to change how we are making them.
On this we agree, but thicker training wheels just gets more people on bikes; It doesn't make the roads any safer.
Better programmers on C can't effecicely eliminate whole classes of the most common fatal security vulnerabilities.
Of course after we do eliminate these low hanging fruit, we will be left with a pie of the remaining classes of vulnerabilities that looks different, it's like Amdahls law. But that's no excuse to skip past "step 1".
> Better programmers on C can't effecicely eliminate whole classes of the most common fatal security vulnerabilities.
Sure they can, it just requires discipline. Most of djb's code (in C) has a lower defect count than most other implementations you'll find in any language, and the mistakes he does make are in relaxing his discipline when thinking it doesn't matter (because of privilege isolation -- something he later admitted was a mistake[1] -- or because nobody puts that much memory in a machine, because times change!).
Zeno would like a word. I'm arguing a different metaphor, not "try harder".
If it is true that programs get too big to maintain the level of discipline the language requires, and regardless of the language you're going to be confronted with defects, then the solution (in my mind) is smaller programs because only the small program has a chance of being correct in the first place.
I agree with your main point, just a nitpick about "at that level of abstraction the “make sure every one of your developers know how to write secure code in C and they never slip up” manifestly doesnt work": it kinda worked with Windows post-XPsp2, the amount of security holes fell pretty dramatically in subsequent releases.
When a company puts security first, they can get results. Unfortunately, security doesn't really sell software like features do, so a true hardened-by-default mindset is impossible in practice. Hence, we need better tools and processes to build features, as you say.
Apple employees would have access to these symbols and much more debugging info that the OP poster "got lucky due to an accidental share", so the process would be a lot easier for them.
EDIT: also, required reading on how vulnerabilities are discovered, important types of bugs, is not unreasonable, nor a long read.
A another C advocate talking about the mythical safe C code that no one has managed to do in 50 years of CVE database entries.
The whole point of safe systems language is not to write 100% code free of exploits, rather to minimize it as much as possible.
Naturally there are still possible exploits, however the attack surface is much smaller when memory corruption, UB (> 200 documented use cases), implicit conversions and unchecked overflows aren't part of every translation unit.
I'm just so happy seeing the gp comment downvoted. It gives me hope that that mentality is slowly dying. Maybe in 30 years we rid ourselves of the C/C++ shackles for something like Rust
Almost all of those are due to code in unsafe blocks. In other words, not safe rust.
A few are cryptographic errors. No argument there, Rust won't save you from that.
FWIW Rust does badly need a standardized unsafe-block auditing mechanism. Like "show me all the unsafe blocks in my code or any of the libraries it uses, except the standard library". If that list is too long to read, that's a bug in your project.
Wow, yeah, that's exactly the technological aspect of what I had in mind.
I guess all that's left is the socialogical aspect: packages' "geiger" status ought to be treated as being as important as their dependencies. In other words, lib.rs/docs.rs/crates.io ought to display these data in all the sorts of places where they list the dependencies of a package.
It would also be great if this tool were made a standard part of cargo. I think it's important enough to deserve that status.
I think this would be a docs.rs or lib.rs feature, I used to think crates.io was that place but it is not.
I could see there being all kinds of scans of dependencies, like enforcing test coverage, builds and tests passing on certain platforms (risc-v, wasi, etc).
No, see, my point is that this is as important as dependencies.
Anything that tracks dependencies ought to be tracking transitive unsafeness.
That's the mindset shift the Rust world needs. Otherwise we're going to keep getting these (in some sense valid) complaints about how Rust isn't memory-safe because it has unsafe-blocks.
Safety is top of mind for a large percentage of the Rust community. There was major political drama when the fastest http framework liberally used unsafe. That framework was almost universally shunned because of it and now the vast majority of the use of unsafe in that project have been removed. I think we are in a good position, but there could be in effect, a Sybil attack against norms where if unsafe was an Ok thing to do for perf or expediency that the value of Rust would be largely obliterated.
My personal hope is that lib.rs and docs.rs replace crates.io and that safety, code coverage, perf and other dimensions of quality are prominently displayed and queryable. Crates.io as it is now has outlived its usefulness.
I believe you and I agree on this issue completely.
I've been looking for a good project to get my feet wet in rust and your idea sounds like a perfect one, but a quick Google search makes it seem like a solved problem. I'd like to understand what's still left that I might add.
> My suggestion is just learn how to write secure code in C
Decades of evidence demonstrate that this cannot be done. Even world experts introduce vulns. Writing secure code in languages with tons of guardrails is hard. Writing and evolving secure C is impossible at almost any scale.
That’s like saying “learn to drive a Formula One car if you want to feel safe driving at 65 miles an hour.” Sure, it works, but it’s impractical and unnecessary for everyone to do this.
Also writing secure C is much harder than driving a Formula One car (as evidenced by the number of competent practicioners of both disciplines in existence).
Yeah, linked lists are bad for the data cache since each element is in some totally random area of memory and thus less likely to be in a cache. Whereas for a linear array the data is next to each other and can be cached effectively and accesses can be easily predicted.
Some 20 years ago, when I was a kernel developer with high performance requirements, my favorite data structure was a linked list of smallish arrays. Each array was small enough to be cheap to completely rewrite on insert/delete, if you needed to e.g. preserve insertion order; most of the time, it was enough to preserve order only across the linked list, treat each array like a hash table slot, or sort at consumption time.
I’ve considered making a vector sorta thing implemented as a linked list of arrays. Assuming you sized the arrays to fit well inside the CPU cache, are there really many tradeoffs?
Even if the array overflows the cache, it's not too bad for most access patterns. CPUs will easily prefetch data on a linear scan, so the "second half of the array" will most definitely be in cache by the time you get there. And a linear scan-once shouldn't even evict too much other stuff out of the cache.
In practice, I would let benchmarks decide the array size. Modern CPUs are surprisingly good at prefetching even linked lists, and x86 works weird out-of-order magic and speculation while waiting for memory. My rules of two thumbs were 1) where performance really matters, don't trust your intuition, program and benchmark alternative implementations 2) don't trust microbenchmarks, e.g. total icache pressure changes whether inlining is good or not.
That's how Clojure implements vectors except its a tree of 8-element arrays. This has nice functional programming properties because it makes it easy to make immutable copies of large vectors with good performance and memory characteristics.
It sounds like you are talking about an unrolled linked list[1]. I'd argue in most cases that while they are better than linked lists in just about every way (except data objects larger than the cache line), in most cases there is a better data structure to use. I'd find that the hashed array tree[2] would likely be a better/more versatile data structure solely due to the improved lookup time.
You can also force-align (with padding if necessary) your structures to be along whatever boundaries make sense for your processor's cache lines.
Ensuring an oft-loaded structure always fits inside a single cache line (instead of splaying across ~1.9 cache lines on average) is good not only for fitting lots of those structures into your L1 but for not blowing out cache bandwidth (read _and_ write, if you're modifying them).
It's much more difficult to prefetch them, though - if you're traversing a large list it misses the cache every time, rather than just the first element.
That's right. The reason why you see linked lists so much in old C stuff is (IMO) similar to the reason you see null terminated strings (and the associated bugs if you truncate incorrectly). And that's because of one of C's original sin: the failure to include size information in array objects.
There is also the claim that linked lists are better in certain types of performance sensitive code because you can sometimes avoid allocating. I don't fully understand the logic there myself but I trust that there are cases where this is true.
The actual reason why you see linked lists (and similar pointer-chasing data structures) so much in old code is because the entire problem of slow-vs-fast memory accesses didn't exist back then. Memory access always happened in a fixed and very low number of CPU cycles (the entire RAM essentially had L1 cache performance). The cpu-memory-gap only slowly started to widen during the 90's when CPU frequencies started to skyrocket and caches had to be added to CPUs. Unfortunately this problem still isn't as widely recognized as it should be in high-level programming circles even today.
If your primary operation is inserting at a random location in the list, linked lists are faster than arrays at large sizes. You avoid having to move all the memory after the index you are modifying (to make space for the inserted element).
A linked list also avoids copying the entire array when you need to insert more elements than you have allocated space for.
> If your primary operation is inserting at a random location in the list, linked lists are faster than arrays at large sizes. You avoid having to move all the memory after the index you are modifying (to make space for the inserted element).
This is false. Big O notation says it should be true, you'll get marked wrong if you say arrays are faster in your algorithms & data structures final, but when you're running on actual hardware the array is faster at all sizes of n and as n becomes larger so does the gap in performance.
Here is a talk[0] by Bjarne Stroustrop (Creator of C++) that even includes imaginary graphs demonstrating this phenomenon. If you want a visual for what the missing graph was supposed to look like, here's a similar one.[1]
Here's another video[2] by Scott Meyers (Author of Effective C++) that goes into more detail about why this happens.
Summary of the Stroustrop video - inserting or removing an item at a point in an array of 100k items needs a linear scan from the start of the data structure to get to the point - an array does that very quickly, a linked list is much slower, lots of random memory indirection, a pointer for every list node blowing out the cache - in practice this linear scan to to the change point dominates the runtime, and arrays come out much faster.
While the array change does need on average 50k items to be shuffled up to make room or close the gap, modern caches are very good at that.
If the array is sorted it can be binary searched to get to the change point, which improves its performance even more, linked lists can’t do that.
I can definitely see in modern cpu array scanning being faster than pointer chasing but I wouldn't have expected that to survive insertion with 50K move wow!
And if you not doing ordered insertion you wouldn't have to move the data in the array anyway, you would keep track of the size and jump to the end, so not sure I understand the binary search comment.
The next question is at what level of growth the waste of empty space in the array becomes too much. Some kind of data structure (tree/linked list) with largish (whatever size applicable for modern cpu) as probably mentioned in other comments does seem the most versatile approach while keeping the performance. Or perhaps the handling of that data structure might overwhelm the array advantage?
> > If the array is sorted it can be binary searched to get to the change point, which improves its performance even more, linked lists can’t do that.
> And if you not doing ordered insertion you wouldn't have to move the data in the array anyway, you would keep track of the size and jump to the end, so not sure I understand the binary search comment.
It just means that if I want to view the nth element of an array, that's a constant time operation. I just take the pointer and add n times the size of the elements.
But for a linked list if I want to view the nth element of the list, I have to view the (n-1)th element first, all the way back until the first element I have a reference to.
> The next question is at what level of growth the waste of empty space in the array becomes too much.
Everything I've tried and everything I've seen from people testing on real hardware is that the gap in performance widens with larger values of n.
You might expect different performance as the system runs out of memory. But arrays have a size of n * element size, and linked lists have a size of n * (pointer(s) + element size), so the linked list would hit memory limitations more quickly regardless.
But the memory usage isn't considering growth. If I grow by doubling the size (seems common) at n+1 element where n is the last allocation, one would allocate n2 + n (the original until the copy finishes) vs. n (pointer(s) + element size).
But yes the size advantage is very reduced for most use cases. What would you imagine the cases where LinkedList is still a valid data structure?
True. Then it comes down to the size of your data vs the size of your pointers.
> What would you imagine the cases where LinkedList is still a valid data structure?
Compared to an array? When the array doesn't have the behavior that you need.
For instance, if you need to keep a stable reference to a particular element in a list, even while that list is being inserted into, then an array is not going to cut it. The linked list handles this case with ease.
That's not to say you can't write a wrapper for the array that does the right thing, probably for cheaper. But out of the box the linked list can do things that the array cannot and if you rely on them, then use the right tool for the job.
I think it's also worth noting that this is specific to (common) CPU architecture where cache is sufficiently big and orders of magnitude faster than main memory, and where main memory reads and writes have similar cost.
The tradeoff in moving memory at the end of the array vs scanning to the point of linked list insertion could be different on some existing and some hypothetical systems where these concepts may be relevant. For example, storage write cycle minimization could be a priority in rare cases.
That said this is an excellent point about the majority of use cases. I hadn't considered the dominance of the time it takes to locate the element to modify. I've never had a reason to benchmark a linked list since even a basic idea of the effect of cache/prefetch precludes its use in every use case I've actually come across.
Probably useful for CS education to start comparing linked lists to bubble sort. There's always a better algorithm, but here's a simple one to learn so you understand how to compare data structure performance.
Are you reading the whole 1 MiB in linear order? If so, then the prefetcher should help in exactly the same way.
If you're not, then why is all of that data packed together? Is there an alternate layout you could use instead where you iterate over the exact values you need? If performance is important to you in that context it might be worth it.
Right - I suppose any large associated data that does not need to be searched can be malloc()'d (via standard or fancy allocator) and you just store a pointer. Or if the other data is relatively small, just in another array.
One case where I don't know what to think: the implications of memmoving the subarray on NUMA cache invalidation in an extremely multithreaded application.
Depends on element size and some other stuff, but if it is a singly linked list, and truely random insertion location, iterating to that location is N/2 on average, where inserting and copying the rest of the array is also N/2. Small elements and the array could still be much faster since they are all prefetched during the copy, vs jumping all over memory during the list iteration and potentially stalling on each for ~200 instructions waiting on main memory.
If a bunch of insertions happen like that, based on some other data structure, and then a periodic iteration eventually happens at some point, you could also imagine an array version being batching together all the array inserts in another simple array and applying them all at once before iteration.
Expanding on this: usually linked lists allocate each element with a separate malloc() on the heap, so they end up all over the place in memory.
You could, in principle, allocate your linked list out of contiguous memory in such a way that sequentially reading the list doesn't cause so many page accesses. That would itself be a complicated data structure to maintain, so simply using a linear array is often the right tradeoff.
That's just a slab allocator. They're not what I'd call trivial, but still simple enough to be common in small projects and CS homework. I agree that an array is still generally simpler though.
Once you are implementing your own allocator you need to worry about fragmentation under many operations, which would be the majority of the complexity.
I was being loose with the words "everything" and "only".
GP comment was curious about why they've only heard about "linked lists" in a language, C, which like Modula-2 does not use them as the basic standard library collections type. When you have to implement linked lists yourself if you want to use them, you're always going to refer to them by implementation detail. In the vast majority of cases where they're used, though, it's as the fundamental standard library collection, and in that case they're usually just called Lists without telling the caller about their implementation.
(This idiom is true to the point that the Python implementers feel the need to point out that CPython uses arrays to implement the list type, as they feel that this would be unexpected to users of other languages that call their basic collections type a "list".)
Finding the bug that allowed this exploit took this researcher weeks. QA can't find all defects without somehow testing every conceivable scenario without knowing every conceivable scenario, and code review can only catch defects if at least one reviewer is able to somehow know that specific methods make an exploit possible. Given that the exact code of underlying methods used may not be known to code reviewers, or that a reviewer might simply not know the full potential use cases for new code at the time of review, it is entirely understandable that defects and resulting exploits happen.
This is why researchers like the OP exist. They find exploits and report them to the manufacturer (hopefully) before they can be used. The fact that this is an effective way of protecting us is also why major software companies offer bug/exploit bounties to researchers.
To demand that all possible exploits of this nature never find their way into production builds is to demand perfection from humans. There is too much to know and think about, and definitely too many unknowns about the future, to make such a fantasy possible while still meeting release deadlines. We software developers often have a hard enough time just meeting feature and documentation deadlines, and adding more people just makes organizing your efforts more complex and difficult which then requires even more people until you reach the point that the scope of organizing your development teams is financially impossible.
I was referring specifically to the list of `IO80211AWDLPeer`s the author was reverse-engineering. His assumption was that the `IO80211AWDLPeer`s were in a linked-list type of data structure (which is a pretty sensible guess). In fact, it ended up being more akin to a priority queue:
> The data structure holding the peers is in fact much more complex than a linked list, it's more like a priority queue with some interesting behaviours when the queue is modified and a distinct lack of safe unlinking and the like.
I amended my post for clarification, I'm sure Apple uses linked lists all the time :)
The scary thing is that even though this sounds like a monstrous effort to pull off this hack, its not out of reach for large governments. Its basically known as a fact they have loads of these exploits sitting in their toolbox ready to use when they have a enticing enough target.
Short of rewriting the whole of iOS in a memory safe language I'm not sure how they could even solve this problem. Assigning a researcher to search for 6 months only to find one bug is financially prohibitive.
The research would've been much shorter if Apple would actually provide researchers with debug symbols. Or you know, if Apple open sourced their security-critical software.
> One of the most time-consuming tasks of this whole project was the painstaking process of reverse engineering the types and meanings of a huge number of the fields in these objects. Each IO80211AWDLPeer object is almost 6KB; that's a lot of potential fields. Having structure layout information would probably have saved months.
> Six years ago I had hoped Project Zero would be able to get legitimate access to data sources like this. Six years later and I am still spending months reversing structure layouts and naming variables.
It’s intensely frustrating, because for some reason Apple thinks it’s a good idea to strip out security code from the source that they do release (months late), and they tend to strip (and until recently, encrypt) kernel code. This is what a company from the last decade might do to hide security issues, except it’s coming from the world’s largest company with a highly skilled security team. Is there some old-school manager with so much influence that they’re able to override any calls from internal and external sources? It’s gotten to the point where Apple engineers privately brag about their new proprietary security mitigations after researchers who scrounge for accidentally symbolicated kernels (thank you, iOS 14 beta) do the work to find them. Why does this situation exist?
There were some Hacker News threads the other day about Marcan's Patreon campaign for porting Linux to Apple Silicon. Everyone basically expects that Marcan will need to reverse engineer everything on his own, and my gut tells me they're right.
But, if you actually stop and think about it for a moment... isn't this situation completely bizarre? Apple Silicon Macs explicitly support booting alternate OSs, because Apple went out of their way to add a `permissive-security` option to the boot-loader. They know Linux is important—the initial Apple Silicon reveal included a Linux VM demonstration—and now a well-known and talented developer is planning to do a native Linux port, at no cost to Apple, and we all fully expect that Apple won't make any documentation available or answer any questions? And, we're probably right?
The more I consider it, the more crazy it all seems. Why is Apple so private about the internals of their products? It won't affect marketing—normal consumers don't care—and I can't think of a plausible scenario where this type of information could help a competitor.
Is Apple using source code stolen from Oracle? Are they scared someone will discover an internal library written in COBOL and make fun of them? Are they worried their documentation could revive Steve Jobs as a vengeful ghost? I just don't get it.
> Why is Apple so private about the internals of their products?
Because they don't care. The extent they care is directly linked to the amount of money they will make from caring. They won't sell more macs if macs can run Linux better; but they will sell more Apple Music subscriptions if macs keep running macOS.
> They know Linux is important
No, they know Linux is a pain in the ass. The bootloader option assuages the executives' conscience enough to be able to talk to a journalist and keep a straight face when asked about "openness" or being "hacker-friendly", stuff those 1980s-style Linux hobbyists keep talking about and nobody else gives a shit about.
Apple makes money by selling iDevices to consumers and selling Macs to enough developers to build apps for iDevices. Everything else is a bonus, and not worth spending much time on. They do the minimum and leave it as that. There is no inconsistency or secret motive. They just don't care. When they cared, in the early '00s, they did a bit more; now they do less. The attitude is the same.
> Apple makes money by selling iDevices to consumers
For now. They haven't made any "I have to buy this right fucking now!" worthy revolutionary improvements in the iDevices lineup recently, the Western market for smartphones is near saturation - and with Corona tanking the US economy for wide masses, people don't have the hundreds of dollars just lying around to shell off for the latest iteration.
I believe that both the last (horribly expensive at that, and still people kept buying it) Mac Pro and the new M1 lineup is a sign that Apple wants to shift back attention to the non-mobile sector - because the competition there is asleep on the wheels. Everyone uses Intel who has managed to fuck up its lineup for many years now, Microsoft has thoroughly pissed off the privacy-conscious folks with Win10 and (judging by a random walk through a Mediamarkt) build quality in Windows laptops still hovers barely above "acceptable" - cheap plastics, tiny touchpads and abysmal screens are the norm, whereas Apple is only robust aluminium cases, giant touchpads and crystal clear, bright screens.
What I'm really excited for is when Apple decides to put an Mx chip into an iMac, paired with a decent AMD GPU. The thermals and energy profile should be allowing a lot more leeway for resource usage than a Macbook...
> they will sell more Apple Music subscriptions if macs keep running macOS.
The type of person who buys an Apple Silicon Mac to run Linux is not going to buy an Apple Silicon Mac to run macOS. However...
> They won't sell more macs if macs can run Linux better.
They would sell some more Macs. Possibly hundreds of thousands more. A drop in the bucket for Apple, but still money—and all they have to do to get it is answer some questions.
Even without being able to compile it I've successfully used their source dumps to debug problems in my code quite a few times (and occasionally find bugs in their code which I have to work around). Having code with comments to read is a huge step up from having to rely on decompilers.
(Quick note for others that my GP comment originally contained a paragraph about the stuff Apple does open source. I edited this out because I felt it was beside the point.)
> P.S. And what's with the stuff Apple does release as open source? Don't get me wrong, I'm glad they do it—because I'll take what I can get—but I have no clue who it's for! A lot of the code is either extremely difficult or impossible to actually compile, because it relies on internal Apple tools or libraries which aren't public
Even when it doesn't rely on anything Apple-specific, it can be unclear how to build it.
I noticed that if I ctrl-z dc, then resume it, it silently exits. I grabbed the source to see if I could build it, and then perhaps debug this.
The source is part of bc. When you extract it there is a directory containing a bc dir, a patches dir, a bc sources tarball, and a Makefile. The bc directory is the contents of the tarball with the patches from the patches directory applied.
Optimistically typing "make" does not work. It runs configure somewhere (in the bc directory, I think), decides that gcc is /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc, and decides that this cannot create executables and exits.
Maybe just going into the bc directory and running configure and make there will do the trick? ./configure works and builds a makefile. Trying to compile with that gets fatal errors, apparently due to missing <string.h> in some C files.
OK, I don't actually care about bc, so how about just trying to build dc, which lives in a subdirectory under the bc directory.
That gets a fatal error due to a conflict between "#define ptrdiff_t size_t" in the config.h that configure made, and "typedef __darwin_size_t size_t" from somewhere. Based on the comments in config.h, apparently it should only be defining that if it is not defined by the system. Commenting it out in config.h and trying again...and all the compiling steps for dc actually finish!
Alas...it then fails because it needs ../lib/libbc.a, which presumably would have been built before building dc if the bc build had worked.
Maybe if I go to ../lib and type make? Nope. In fact, the errors are identical to when I typed make for bc, because it turns out that making libbc.a is the first thing the bc make tries to do.
Tossing in "#include <string.h>" in lib/getopt.c and lib/number.c makes everything build, finally giving me a locally built dc.
Is it too much to ask that when I download the source from Apple to their version of a simple command line knows-nothing-about-MacOS utility like this, I should just be able to type "make" somewhere and have it build? Or at least have a README in the tarball that tells me what I need to do?
In this case, the top-level Makefile includes a bunch of internal junk, and the configure script thinks your system is very broken because it's old and Xcode 12 ups the warning for a missing prototype to an error. I was able to get it to build with
$ CC="clang -Wno-implicit-function-declaration" ./configure
$ make
I can only speculate, but Apple seems to have very tightly coupled software and hardware. Since this coupling probably holds trade secrets (which we don't know about by definition), it seems likely to me that they are controlling access to as much of the stack as they can while still protecting those secrets.
Yes, but that doesn’t really make sense for things they have already shipped: researchers have to reverse engineer those for what seems like no reason. For example, the newest iPhones have entirely custom privilege levels that are lateral to the typical ARM exception levels and entered using proprietary instructions that their own silicon understands. This is something you can find if you load the kernel into a disassembler and poke at it a bit. But Apple doesn’t mention it at all or document it…what’s the point? Why put up such petty barriers in the face of people trying to audit this?
Apple doesn't really obfuscate their code outside of their DRM stuff–usually they just remove all the symbols and do a ⌘F for certain terms in their open source releases and strip those out. It really seems to be an manual process, since sometimes they miss things…
> Apple thinks it’s a good idea to strip out security code from the source...
Because it makes it easier for attackers to find vulns. That's why they do it. The high payouts on these platforms are evidence that it isn't as simplistic as "only defenders would look at this why won't they release it!?"
But doesn't it work in some ways? It's not going to save them, but it seems to significantly increase the time/cost of exploiting the vulnerability. One more layer to the security system.
Obfuscating source? No, not at all. It just annoys legitimate security researchers (making them not want to deal with you) and is something that black hat bug finders largely don't care much about. Not only do they have more resources and patience, they are also more willing to use questionable methods to make their lives easier.
What makes it less of an issue for black hats? Do they have access to symbols/source code that security researchers do not/are not willing to use?
I certainly understand the frustration for legitimate researchers, and there's plenty to be said about having the source code available to make auditing easier but in itself it seems that making a black hat take 6 months instead of 1 to create an exploit raise the skill/patience level needed and busy them for while where they are not working on the next exploit.
Yes: black hats have much more incentive and generally larger, more focused teams to find these bugs, and they aren't concerned with the issues of buying stolen devices and source code on the black market. (If you're curious, search for "dev-fused iPhone" and "iBoot source code". The Project Zero team works from about the worst situation possible, choosing to even forgo using services like Corellium.)
> It looks like we won't be able to use the Apple "Security Research Device" due to the vulnerability disclosure restrictions, which seem specifically designed to exclude Project Zero and other researchers who use a 90 day policy.
Advanced users that want a secure device require devices that can be reinitialized to a known state without external input.
This is no longer possible on any phone, tablet, or computer Apple sells: all require online activation with device-specific info. There is no way to put the device back into a known state offline or without Apple having an opportunity to tamper with it (or be forced to tamper with it).
> This is no longer possible on any phone, tablet, or computer Apple sells
It is still possible on all of their computers, just not their phones or tablets. Intel Macs (which are still being sold in large numbers) can always be wiped and restored from USB without an internet connection, and Apple Silicon Macs can do it if you set the boot-loader to "Reduced Security" mode.
This is a false statement. Intel macs have the T2 boot security chip, which requires online activation to be able to access the internal disk after a full system wipe. The M1, even in reduced security mode, also requires online activation after a full system wipe. I've tested this this week; if you know of something I'm missing please tell me the exact steps to take to wipe and reinstall a T2/M1 mac offline, as I am confident now that it is not possible to do so.
I would love to be wrong about this.
This is the case even if you have a full offline boot/restore USB.
I have a post coming out today about just this, and how it renders all current macs unsuitable for long term offline/airgap applications.
These are just phones that you are officially permitted to attach a root shell and kernel debugger to, like to any other device that's not an iPhone. Researchers have been working around that for years by using private jailbreaks / exploits to get similar levels access, and with checkm8/ktrw you yourself can get similar access to any vulnerable iPhone 7/8/X.
No sources or structure layout or symbols, so you're still stuck waddling through megabytes of compiled code to reverse-engineer everything from scratch.
It's Apple drumming up absolutely nothing, and from my point of view it's mostly a PR stunt.
> It's Apple drumming up absolutely nothing, and from my point of view it's mostly a PR stunt.
Well, I don't think it's quite "nothing". Newer phones don't have access to checkm8, and getting a private jailbreak or exploit working can be non-trivial. And in some cases, researchers may need to avoid reporting that exploit to Apple in order to keep using it.
It's a good step. It's just not sufficient, especially given all the other restrictions.
> And in some cases, researchers may need to avoid reporting that exploit to Apple in order to keep using it.
And this will continue to happen until Apple just starts selling the damn things to anyone who wants them, instead of trying to gatekeep them to people who are playing by their ridiculous security disclosure rules.
Right! It would solve so many issues! Put them on an unlisted page of your online store, charge a 50% markup over a normal iPhone, make the boot screen bright red, and do something ugly and obvious with the phone's exterior.
Sure, some crazy people who aren't security researchers will probably buy them too and use them as daily drivers (I'd probably be one of them). So what? I don't understand why Apple feels the need to hold this stuff so close to their chest. Everyone in this scenario knows exactly what they're buying.
Oh, that's a shame. The slide in the referenced tweet says, "advanced debug capabilities", so I'd assumed that's what it meant. I wonder what else that could mean?
The ability to attach a debugger to the kernel. No, really, that’s “advanced” for an iOS device, because normally you don’t get to do anything even close to that. You can’t even debug userspace processes that aren’t ones that you put there yourself (as a developer writing apps) on normal iPhones.
Believe it or not, open sourcing the security code is actually not a great idea. Most of the worlds bot nets run on Wordpress which is open source. Most of the time legitimate actors are not going to read through an entire code base because they have better things to do. Illegitimate actors however have a very high incentive to read through a widely used public code base and do so.
By 'wanting to prove something', he caused the vendor to act urgently, instead of sweeping this as a maybe-exploitable-maybe-not bug that would get lazily patched whenever.
By 'wanting to prove something', he showed the shortcomings of multiple security mitigations, all defeated by simple bugs.
By 'wanting to prove something', he also discovered two other exploitable 0days, that wouldn't have been discovered otherwise. Those 0days were likely already in the hands of bad actors, too.
Finally, the reason he even discovered the original bug is because Apple accidentally once or twice forgot to strip function names from a binary. If this didn't happen, that bug very likely would still be out there in the wild.
I'm not sure you understand how security research works.
This is a weird statement, since the premise of this blog post is that these kinds of attacks aren't out of reach for a single talented researcher on a Google salary. It's not out of reach for any government. Nauru, Grenada, Tonga, the Comoros --- they can all afford this.
I believe the point of SulfurHexaFluri's final sentence is that it is cost prohibitive for Apple to dedicate a bunch of employees to search for bugs in order to fix them all. That is, it's cost-effective to find 1 bug, but not to find all of them. The sentence could have been worded better.
I'd personally phrase things a bit differently: an _individual_ was able to pull this off while surrounded by screaming children. A large government, with all its resources and hundreds+ of people, would pull this off regularly and without breaking a sweat.
> Short of rewriting the whole of iOS in a memory safe language I'm not sure how they could even solve this problem. Assigning a researcher to search for 6 months only to find one bug is financially prohibitive.
Note that memory safe languages won't solve security. They only eliminate a class of security bugs, which would be amazing progress, but not all of them.
Didn't they move WiFi drivers, among other things, into the userspace in macOS Big Sur? I've heard somewhere that they're going in the direction towards microkernel for this particular reason of reducing the attack surface.
(yes I know I'm talking about macOS but the vulnerability was found in iOS, but there's a lot of shared code between them, especially on levels this low)
Google "NSA TAO" -- Tailored Access Operations. AIUI, among other things they're responsible for developing, discovering, and weaponizing exploits used to access high value targets -- sometimes through fun techniques like "Quantum Insert", a sort of faster-man-in-the-middle attack. The wealth of exploits released in the equation group hack should put all doubts to rest.
There's a market for exploits that pays pretty well. Someone is throwing millions of dollars at them, and from what we can glean from investigations, leaks and whistle blowers, it's states that are buying them. One company in that space made world-wide news[1] by selling to governments.
Because that is the engine, the cosmetic is irrelevant. Unless you are choosing for the interface but that is not the concern of 99% of the people. We need a real independent searcher
The whole NSA leaks thing proved it. They had a tool built for exploiting windows boxes which was leaked and converted in to the ransomware WannaCry which spread globally a few years ago.
I believe they described their toolbox as metasploit on steroids. Some other episodes of darknet diaries also interview former and current government hackers.
It is not just not out of reach for large governments, it probably not even out of reach for most organizations with between 5-10 people. As the author says, 6 months of "one person, working alone in their bedroom, was able to build a capability which would allow them to seriously compromise iPhone users they'd come into close contact with". Even if we assume the author is paid $1,000,000 a year that is still only $500,000 of funding which is an absolute drop in the bucket compared to most businesses.
The average small business loan is more than that at $633,000 [1]. Hell, a single McDonalds restaurant [2] costs more than that to setup. In fact, it is not even out of the reach of vast numbers of individuals. Using the net worth percentiles in the US [3], $500,000 is only the 80th percentile of household net worth. That means in the US alone, which has 129 million households, there are literally 25.8 million households with the resources to bankroll such an effort (assuming they were willing to liquidate their net worth). You need to increase the cost by 1,000x to 10,000x before you get a point where it is out of reach for anybody except for large governments and you need to increase the cost by 100,000x to 1,000,000x before it actually becomes infeasible for any government to bankroll such attacks.
tl;dr It is way worse than you say. Every government can fund such an effort. Every Fortune 500 company can fund such an effort. Every multinational can fund such an effort. Probably ~50% of small businesses can fund such an effort. ~20% of people in the US can fund such an effort. The costs of these attacks aren't rookie numbers, they are baby numbers.
For those who don't see why a company would want to use such exploits, consider how valuable it would be to know if a company's employees were planning to organize or strike.
There are also paranoid people in positions of power, and bureaucracies that can justify spying on employees. One of the interesting things about this lockdown was finding out that many companies put spyware on their employee-issued computers to monitor their usage.
How is it financially prohibitive to pay a researcher a salary to find a 0day like this, where the bounty programs pay $100k-$500k on 0days on the same ?
source: https://developer.apple.com/security-bounty/payouts/
Unfortunately, it's the same old story. A fairly trivial buffer overflow programming error in C++ code in the kernel parsing untrusted data, exposed to remote attackers. In fact, this entire exploit uses just a single memory corruption vulnerability to compromise the flagship iPhone 11 Pro device. With just this one issue I was able to defeat all the mitigations in order to remotely gain native code execution and kernel memory read and write.
Yes, same old buffer C/C++ overflow problem. We have mainstream alternatives now. C#. Go. Rust. It's time to move on.
The code where the bug happens is legal C++, but it uses absolutely none of the memory safety improvements which were added to the language in the past... twenty years probably. It's basically C with classes.
If they haven't kept up with the changes in their current language, what makes one think that they would "move on" to the alternatives, two of which aren't even alternatives?
Before they switch to Rust it would be much faster and more efficient to use smart pointers, std::array, std::vector and stop using memcpy.
Note that this code is shipping as a kernel extension, which uses Embedded C++, not standard C++. Notably, things like templates and exceptions are not available. It would be nice if they could work on this instead, but looking at the dyld and Security sources (which has no such limitations, as the run in userspace) I don't have much confidence.
I agree, that was the path taken by Solaris SPARC and it is the only way to make it work, because even if a language level safety would be introduced today, not everyone would bother adopting it.
As much as I like to bash security critical code written in memory-unsafe languages, I don't think that this is the crux of the problem here.
To me it's that this extremely trivial bug (the heap overflow, let's ignore the rest for now) passed through code review, security review, security audits, fuzzing... Or that Apple didn't have these in place at all. Not sure which option is worse.
We have 30 years of experience showing that ordinary heap overflows are not in fact easy to spot in code review, security review, security audits, and fuzzing. Each of those modalities eliminates a slice of the problem, and some of them --- manual review modalities --- will remove different slices every time they're applied; different test team, different bugs.
To me, this strongly suggests that the problem is in fact memory-unsafe languages, and not general engineering practices.
Apple, by the way, has all the things you're talking about in place, and in spades.
OTOH, we don't really have evidence to show that memory safety is effective in kernels/drivers because no memory safe language has ever been deployed at scale for that purpose.
> the problem is in fact memory-unsafe languages, and not general engineering practices.
Languages don't introduce bugs by themselves. Engineers produced those bugs.
I always thought that bugs are the programmers' fault, and not to blame the language. It's like blaming the English language because it allows you to misuse it and manufacture very offensive racial slurs, or to be rude and cruel, and thus we should replace it with another language that doesn't allow to exploit these weaknesses. We won't be able to express ourselves with beautifully (low-level) crafted poems anymore, but that's the price to pay.
There are inherent features of human languages that force you into weird issues. English for example has gender pronouns and that's why you see in profiles how you should approach someone. It's not like they want to add it, it's that they have to if it bothers them when people misuse them.
In Hungarian we don't have this problem at all, there's no concept of gender specific pronouns.
Such bugs are extremely difficult to prevent at scale. Even the most talented engineers make such mistakes and programming quality varies significantly even within top engineering teams which are usually comprised of people with different skill sets (+ junior engineers that need training).
Safe languages are the only way forward to drastically reduce the problem. It can’t be guaranteed to be eliminated 100% obviously because there are still escape hatches available, but it will be significantly improved because you can limit the surface area where such bugs can live.
Not sure if this is common for everyone but I find whenever I get assigned for a review for a monster change, I spend over an hour just working out what the change does and if it seems like it will work. There is no way I could spot tiny potential security exploits in 3000 lines of changed code.
Sending 3,000+ LoC code reviews is not considered good engineering practice. In general it's important to keep code reviews small for the reasons you describe. If it's impossible to make incremental changes in smaller units, that's a sign of an unhealthy code base.
Although it is certainly challenging, you shouldn't spend only an hour reviewing a 3,000 LoC review, unless most of it is trivial refactoring, especially if that code is security-critical and handles untrusted input. That's only 1.2s per line of code. No chance you have good quality control with that amount of attention.
If it's taken someone a whole fortnight to write it, you should expect to spend at least half a day reviewing it, IMO.
Does any software producer do fuzzing on their own product? I have never heard of this being done by software developers. Usually it's done by exploit developers. Of course there are static analysis tools that should uncover a problem like this, and I know that high-reliability embedded software developers use them, but I don't know if the likes of Apple does.
There are systems languages and then there are systems languages. As the Golang team have pointed out, there's lots of systems programming going on outside of OS kernels. Neither of those links mention kernel development. Pervasive refcount updates (ARC) and a vtable-unfriendly dynamic dispatch mechanism inherited from Objective-C are fine for userspace, but most kernel code is very performance-sensitive.
Go team only changed their message due to the pitch forks they were getting from the UNIX/C crowd.
There are people that believe systems programming languages in an OS kernel is doable with some form of GC, and then there those that will never change their mind.
I'm familiar with Biscuit and Midori. The Biscuit paper estimates a 10% performance hit in syscall-intensive benchmarks due to using Golang vs. C.
That being said, ARC may faire much better than a tracing GC, particularly in latency variance, throughput variance, and mean heap usage.
Though, I think we're best off moving off of hypervisors and onto 4th generation microkernels with isolation similar to AIX LPARs / Solaris Containers. After all, a hypervisor is essentially a microkernel that's forced to use hardware traps/faults plus hardware emulation for its syscall interface (plus upcalls for performance, which use a calling convention much closer to traditional syscalls). There are stability, security, and performance advantages to throwing out all of that hardware emulation code, moving everything to upcalls, and getting rid of the second (guest OS) kernel running between the application and the hardware. If you push most of the system functionality out of kernel space, then rewriting some of the less performance-critical components in a language with ARC or tracing GC starts to make more sense.
ARC is slower than tracing GC as all benchmarks where Swift was used prove.
Biscuit was a research project whose goal was to get the thesis done, when the thesis was done, no more effort was spent.
Bing was powered by Midori for the Asian countries for part of its life, and Joe Duffy has stated in the RustConf keynote that Windows Dev team was a reason why Midori faced so much internal resistance, even when they were proven wrong.
Before Google was willing to put the money into JavaScript many would assert that JavaScript would be worthless to anything besides form validation and DHTML.
Mainframes to this day still make use of their own systems programming languages, way safer than C, and you don't see people crying that their kernels are too slow.
In fact, Unisys uses this fact to sell ClearPath MCP to customers that value security above anything else, Fort Knox style.
Very interesting! I wasn't aware Midori had been widely deployed in production! My understanding is that Midori ran the kernel and all programs in ring 0 and relied on the classloader enforcing type safety and the managed runtime enforcing the other security and stability constraints normally enforced by hardware, so syscalls were just normal method calls and there were no context switches.
Burroughs MCP was written in essentially an extended Algol 60 dialect. Algol 60 only had very limited heap allocation for dynamic arrays, no GC, and I haven't read any indications that Burroughs added GC to their extended dialects.
Multics was written in a PL/I dialect, without tracing GC. Likewise, IBM OS/360 and descendants are written in the PL/S dialect of PL/I, and I haven't seen any indication it has tracing GC.
With tracing GC, you have a trade-off between the peak amount of unclaimed garbage and the GC overhead. ARC should have lower variance in both latency and heap usage, which I presume is the reason Apple moved the whole Objective-C and Swift ecosystem to ARC and deprecated the Objective-C tracing GC.
I used to be a True Believer(tm) in the JVM and other managed runtimes. I was one of 5 developers of the most popular Java desktop application in the mid 2000s. Then I moved to Google and started developing web search infrastructure. I was at Google when V8 was created, and I put a lot of effort into running all of the JavaScript that the indexing system found, across the entire visible web. For things at massive scale, spending millions of dollars per year just in electricity bills, it's extremely tough to beat highly tuned C++. Yes, it's a lot of effort. Yes, I hope safer languages like Rust replace C++ and static analysis tools continue to improve.
I still kind of want to be a managed runtime true believer again, but it's tough to go back after believing for so many years that managed runtimes were going to match expertly hand-optimized C++ in latency- and throughput-critical applications "any day now".
As for the mainframe languages, yes they don't have a GC, but they have the right defaults regarding bounds checking, implicit conversions, explicit unsafe code.
Regarding managed runtimes, versus C++, languages like C#, D, Modula-3, Swift have the features to write C++ like code when needed, the main problem is that many don't bother to learn the language features available to them.
At Microsoft stories about hard core C++ devs having to be proven wrong with C# running in front of them is relatively known, Joe Duffy has shared a couple of such stories.
His experience in Singularity and Midori is also what made him bet on Go for Pulumi.
Has Chris specifically mentioned OS kernel programming?
As the Golang team have pointed out, there's lots of systems programming going on in userspace, which is what they refer to when they call Golang a systems programming language.
That being said, ARC is probably easier to get to work well in-kernel vs. tracing garbage collection. There's a performance cost to all of those reference count updates, but at least the variance is extremely low.
That's an interesting experiment, but that's all it is. The project relies on ASM/C/C++ to boot into a microkernel and to interpret and run the C#. But I suppose it would greatly reduce the attack surface of C/C++/ASM code.
I just wonder, for example, how a capable hardware abstraction layer would work in C#, interrupt handling, CPU and IO scheduling, etc.
C#, Go, and Java all go in the same category (roughly)—they wouldn't work for kernel code. Rust will be a valid replacement for C++ kernel code in the near future, I'm sure.
> you definitely cannot build a performant and robust kernel from scrtach with these languages.
I think this is irrelevant (and less importantly, false). Its's fine to use eg se4l as a foundation, or as a incremental step even just have a safety focused driver runtime inside the kernel proper.
Possible, perhaps, but feasible? Microsoft certainly had a go at it with the likes of Midori and Singularity, but these were met with the same fate that will likely befall any managed code kernel. While it's an honorable pursuit with certain merit, to produce a fully featured OS in this way -- without serious concessions -- is just not feasible.
C# is GC'd so massive memory hit, and also not a language you can have in a kernel.
Go: GC again, so no go.
Rust: most sane of the examples you've given.
Apple has already started migrating to Swift which is a memory safe language.
However the real reasons Rust and Go aren't feasible is that they're both essentially all-or-nothing, and neither offers even the most basic semblance of ABI compatibility. Their only nod to ABI stability is "use FFI to C" which means your APIs remain unsafe, and doesn't work for non-C languages without all your system APIs having other languages layered on top.
Swift at least lets you replace individual objc classes one at a time, and is ABI stable, but has no C++ interaction.
Yes, but what about these huge legacy codebases like the iOS kernel? I assume we will have to deal with this type of vulnerability for years to come...
I had that argument with the C standards people a decade ago. [1] Consensus was that it would work technically but not politically. The C++ people are too deep into templates to ever get out.
The basic trick for backwards compatibility is that all arrays have sizes, but you get to specify the expression which represents the size and associate it with the array. So you don't need array descriptors and can keep many existing representations.
Also, if you have slices, you rarely need pointer arithmetic. Slices are pointer arithmetic with sane semantics.
I'm tired of seeing decade after decade of C/C++ buffer overflows. It speaks badly of software engineering as a profession.
The political aspect is why I suggest the solution is to just up and fire all those guys. More realistically Microsoft, Apple and Linus could just force the issue. Gets added to Visual C/C++, LLVM, and Gnu C as an extention. And then start polluting code bases and API's with it.
Microsoft is already kind of doing it, if you compile in debug mode, you get bounds checking in all STL types, and you can enable them in release builds as well.
But yeah, it only works if you use those types.
There are other divisions pushing for .NET and Rust systems code in Windows, but the political wars between WinDev and DevTools are quite well known, e.g. Longhorn (in .NET) vs WinRT (same thing just in COM).
Proper thing is bounds checking should be by default and you actually have to turn it off for hot path code. Because 99% of the code people write isn't memory or CPU bound. If a little used code path is sometimes throwing bounds exceptions you want that to get logged in production.
Check C.A.Hoare Turing award speech in 1981 regarding the use of bounds checking in the 60's and how customers of Algol compilers considered a legal offence to even turn them off.
I'm not sure what exactly your are trying to say. As far as I can tell, there are indeed safe variants for arrays in the standard - both static and dynamic. People just choose to not use them for some arbitrary reasons.
> What's more, with directional antennas, higher transmission powers and sensitive receivers the range of such attacks can be considerable.
I'm reminded of ye olde Gumstix BlueSniper rifle. Back in the early 2000's there were a series of exploits against bluetooth stacks. The standard response by the industry was that they attacks weren't practically exploitable due to the low power of typical bluetooth devices.
The BlueSniper was a cantenna + gumstix SBC specifically constructed for the purpose of demonstrating the low cost of the threat.
Apple sits on this giant stack of unused money [1]. Why don’t they get the best security researchers in the world, pay each of them north of $1M / year in salary and create the ultimate red team where their only task is to try to hack Apple devices.
If they get a team of 1000(!) people, each with $1M(!) in salary that would be less than 0.5%(!) of their revenue in 2019 [2].
There are dozens, perhaps hundreds of people working at the level we're talking here --- vulnerability research is highly specialized. So the better question is perhaps why Apple doesn't build a program to train 1000 researchers to compete.
I get the impression that while Apple is world-class at HW ops, they are very mediocre at people ops. (and I get the impression that Google is the opposite)
I guess. Project Zero has a sort of unique history; as I understand it, it's less a reflection of Google's distinctive culture as it is Google's savvy in acquiring and nurturing a pre-existing research culture, and that might not be replicable. But you can also ask the question: how much of an impact has P0 had on shielding Google and its partners from similar vulnerabilities? If your impression is that, because of people like Ian Beer, Android phones are basically impregnable, I'll submit without a lot of insider knowledge that you're probably mistaken.
What an Apple P0 buys Apple might just be a bunch of favorable nerd press cycles. But that's not a problem Apple really has.
I am, however, convinced that with the right resource commitment, you could scale up a world-class research capability --- to potentially arbitrary levels --- without headhunting existing researchers, which is where I see the bottleneck right now.
Or, I mean, Apple could just rewrite their OS infrastructure in a memory-safe language. If I had the two options, I would put all my chips on the language change.
(I think P0 is extremely cool and valuable to Google in a bunch of ways and would be thrilled to see more major vendors try to replicate it, even I doubt they'll be successful).
I think you're very _very_ wrong about people ops at Apple.
The reason why Apple in 20 years turned from being 90 days away from bankruptcy to a revolutionary machine and most valuable 2+ trillion dollar company in the world is not because of HW ops or anything else, it's because of people.
While we know Steve Jobs had "issues with people", he also clearly stated:
> My model for business is The Beatles. They were four guys who kept each other kind of negative tendencies in check. They balanced each other and the total was greater than the sum of the parts. That's how I see business: great things in business are never done by one person, they're done by a team of people.
It takes a lot of people effort, talent and operations to achieve what Apple has achieved. So I think saying Apple is mediocre at people ops is unfair.
I am just surprised because there are so many problems in tech where throwing money at it is not going to improve things.
However in this case, shouldn’t they be able to attract the best in the world just by turning the money gauge up?
If you are one of the most highly specialised vulnerability researchers in the world, would you seriously reject a $10m / year offer from Apple where you’d be able to spend all your time doing what you love with the only condition being that you report findings to Apple?
It mystifies me too. I'm an independent security researcher that currently has a vulnerability in macOS with grave implications. I'd like to sell it to Apple for a fair price, but their security email is a dead end. Every time I've reached out they want me to disclose all of my research up front, no price negotiation. After doing as many bug bounties as I have, I've been burned one too many times by companies giving ~$200 for weeks or months of effort (less than minimum wage of course) on P1/P2 vulnerabilities in their infrastructure. I'm talking to a few groups who are willing to negotiate a price with me, but I can't be sure of their intent. I want to get it patched, but it's difficult when Apple themselves are disinterested.
Do you have any reason to think that Apple could stiff people that submit vulnerabilities to them?
My understanding of game theory says that Apple’s incentives are to try to act with integrity and to pay their bounties. There may be corner cases where confusion reigns, and where Apple mistake someone for a fraud, but I would presume they need to be very rare – otherwise Apple’s reputation as a buyer would suffer and people would sell to other buyers who cared for their reputation better (and every vulnerability sold to a third party has a high expected cost to Apple. Edit: on second thoughts maybe the cost to Apple is fairly low - certainly the maximum bounty size says that).
Edit: I agree that Apple stating a maximum payout is hardly helpful. I presume third party buyers indicate a minimum value they will pay depending on the value of the vulnerability to them. There is a market here, and it isn’t clear that Apple is willing to pay market prices, perhaps because too many people/teams give their vulnerabilities to Apple for $0 (e.g. projectzero!)
I think it's more complicated that just what they list on the bounty site. In this case the parent commenter has to provide all of their work to Apple, before discussions of what it's worth. Additionally, it's not like there is a clear and transparent market around the bug bounty market. Unlike the Chrome bug program which releases all of its reports, discussions, and payouts after ~90 days or so, there's no way to see the history of what's been reported to Apple.
You can hire all of the smart people willing to work for you, but there will always be someone smarter not able to join you. That's either because they don't like you, or something else preventing them. Either way, you cannot guarantee that you will catch 100% of the vulns 100% of the time.
No, because there is no reason to assume that would materially improve security. Do you think a bulletproof vest manufacturer hiring the best gunmakers in the world would dramatically improve their bulletproof vests? It could help, and it is certainly essential to have good bullet/gun engineers on staff, but you would probably be better off hiring people who know materials science and the actual job of making bulletproof vests.
It would be far more beneficial for them to just use the tried-and-true techniques that have already been deployed for decades in high-reliability/high-security systems. In the event that such things are too onerous, they could run development methodology tests to remove the elements that provide the least security ROI to produce lesser, but still good, systems at a reduced cost. This would be far more likely to produce a good outcome than taking the standard high development velocity commercial methodology that has failed to produce meaningful security despite decades of attempts and enhancing it to be a high security process. At least in the former you can be reasonably confident you get good security, though possibly at a higher cost than desired. In the latter, although the cost may be less, the security is a complete unknown since you are using a new process invented by people who have never used, let alone made, a high security process before and it is a class of strategy that has literally never succeeded over multiple decades of attempts. Not to say it could not happen, it took hundreds or possibly even thousands of years of failed attempts before heaver-than-air flight was cracked, but they would probably be better served just using the existing techniques that are known to solve the problem.
Because there are always more bugs to be found in unsound software.
This finding is not about this single bug, it's just that someone bothered to scrape the surface.
(Note that 99% of the effort went into crafting the demo exploit once the vulnerability was found, which is basically wasted effort in the context of eliminating vulnerabilities - the vulnerability finding was easy)
well they might be trying. they recently hired Brandon Azad from p0, who definitely is up there. The problem is, that a lot of high calibre security people simply don't want to work for Apple. It suppose its out of spite for all their shitty policies..
I am actually not convinced about your assumption that it wouldn’t make them any money in the long-term.
My theory is: people that are quite tech savvy (like the HN crowd) would look at such an effort quite favourably and these folks are often micro-influencers when it comes to buying decisions of their direct peers.
Just an anecdote, but my entire family uses Apple devices, because I am the go-to computer guy in that circle and I advised them to buy Apple. The company that I co-founded used Apple hardware and so on.
Maybe that is just wishful thinking and it is hard to quantify, but I’d like to believe that increasing your reputation with developers (who in itself are a niche) helps you grow revenue in the long-term nevertheless.
I mostly agree with you. I'd like to point out this seems to be the first project zero post on HN that hasn't had a handful of posts suggesting project zero is a hit squad going after Apple.
Well your anecdote doesn't seem to support your argument.
Google is the one paying all those researchers at Project Zero, Apple doesn't seem to have that kind of security group, and yet you still buy/recommend Apple instead of Android.
I am sure it can move some people, but the reality is that this kind of effort is so down the list of priorities when buying for most people that it is certainly not worth $1B per year.
People need to accept that the problem is the language. We will never solve the developer problem, but we will/can/have produced languages that make these types of errors impossible/extremely unlikely.
'AWDL is an Apple-proprietary mesh networking protocol designed to allow Apple devices like iPhones, iPads, Macs and Apple Watches to form ad-hoc peer-to-peer mesh networks.
...
And even if you haven't been using those features, if people nearby have been then it's quite possible your device joined the AWDL mesh network they were using anyway.'
Wow, so Apple was ahead of Amazon's Sidewalk with AWDL. Can you disable this?
> Wow, so Apple was ahead of Amazon's Sidewalk with AWDL.
Not exactly. The wording in the article implies that AWDL forms some kind of multi-hop network topology, but it doesn’t - it just enables nearby devices to communicate with each other directly at Wi-Fi speeds without the burden of pairing (like Wi-Fi Direct) or being associated with the same Wi-Fi network.
This is used not just in AirDrop but also in the Multipeer Connectivity Framework, AirPlay 2 and the Continuity framework. The standard discovery mechanism for these services is mDNS over AWDL, so for a device to browse for or advertise these services, it needs to be aware of other nearby AWDL neighbours first. (For example, you can browse for and discover other nearby AirDrop devices even if you don’t allow incoming AirDrop enabled yourself.)
It’s also worth noting that Apple devices very strictly do not send or receive AWDL traffic when they are locked/asleep, and will often even stop listening on the AWDL social channels when there are no services being advertised or in use.
It looks like disabling airdrop doesn't do anything:
> All iOS devices are constantly receiving and processing BLE advertisement frames like this. In the case of these AirDrop advertisements, when the device is in the default "Contacts Only" mode, sharingd (which parses BLE advertisements) checks whether this unsalted, truncated hash matches the truncated hashes of any emails or phone numbers in the device's address book.
Then follows the section on brute-forcing 2 bytes (only) of a SHA256 hash.
I don't think that proves what you think it does - that's with AirDrop on, but in a limited mode.
If you turn AirDrop/Bluetooth off, you may well disable this.
On my phone:
- If you disable Bluetooth in the notification tray, then it goes to Bluetooth "Not Connected", but not Off.
- If you disable Bluetooth in settings, AirDrop automatically goes into "Receiving Off".
- If you then enable AirDrop, it'll automatically turn Bluetooth on.
So I don't think it's true that you can't disable it - unless the UI is misleading about Off.
A bit OT - how do I work on developing the skill set necessary to find vulnerabilities like these? Should I take some particular courses, or some other “track” of sorts? At the moment, I have an undergraduate in Computer Sciences, and I’d say I’m a fairly OK programmer.
Check out LiveOverflow on YT. Maybe play some CTFs, but don't do that super seriously, just enough to get you hooked on binary exploitation. They're fun, especially if you find some teammates to cooperate with.
And then just, well, practice. A lot of practice. Mostly driven by curiosity about how things work - bugs will then just start to pop up and you are free to investigate whatever piques your interest. The more likely you are to just open up a debugger when a piece of software annoys you and try to binary patch it, the closer you are to being a security researcher :).
There's not much books/courses on this, low-level hacking is something that you kind of just learn as you go. But, for instance, if you never touched gdb/lldb, or never looked at assembly code, or never wrote C - you should investigate that first as base skills.
There is an excellent pre-packaged VM with levels of challenges that take you through the basics of exploitation to quite advanced levels called "Modern Binary Exploitation" [0]. I would highly recommend it.
You can also do the challenges using IDA/Ghidra instead of looking at the source for a proper challenge and I recommend doing this initially for each challenge.
I'd recommend CTF'ing a bit stronger than the other commenter. While there can be a distinct gap between the vulnerabilities in ctfs and real world applications, CTFs provide a great means of deliberate practice (work on a problem, potentially figure it out, and then read other peoples' write-ups after the competition ends).
I didn't meant to discourage from playing CTFs, I just became jaded by seeing the same kind of heap feng shui tasks over and over and over again :). You know, the note-management linked list task with a simple CLI menu. Not to mention the proliferation of 0/1day tasks, which are IMO just lazy.
Do play CTFs. Just pick the fun challenges. pwnable.kr used to have some good stuff if you want to level up.
I think we're on the same page. Once someone gets good enough at heap shenanigans, they likely have a good enough skill baseline to go after real targets. In terms of skill development though, I found ctf'ing gave me a decent sense of what may be exploitable, that it would be hard to get otherwise.
It would be amazing to plot the 2.4 GHz amplitude vs. time series plot of this exploit.
Think about it, an ocean of electrons in the copper WiFi antenna bump along with a certain guiding EM wave and in so doing, they inadvertently cause the information moving electrons in the silicon crystal to disconnect from the electrons being pushed out of the Li-ion battery.
This amplitude fluctuation in principal could have been broadcast by motions of stars in the universe, as astronomy does peer into the deep with these frequencies [0].
In the future, one could imagine a bad actor with control over a global network of low orbit satellites spewing out this code for decades preventing the such devices from being turned on long enough to receive updates, deactivating billions of dollars of human capital.
Probably more than a hundred; there are teams of dozens at the good corporate security groups and an unknown number working for governments and other organizations that don’t appear as publicly.
The link to the clang pointer auth doc is broken, Apple changed their default branch name to 'main' instead of 'master'. A (more?) permanent link is [1].
I checked and airplane mode seems to disable wifi and 4g but not bluetooth. Airdrop refuses to work without wifi. Not sure to what extent wireless is actually turned off for airplane mode now though.
Both Wi-Fi and Bluetooth are usually turned off by default when you turn on airplane mode, but you can turn on both without turning off airplane mode, at least in my experience.
The article explains bypassing exactly this (PA/PAC).
> Vulnerability discovery remains a fairly linear function of time invested. Defeating mitigations remains a matter of building a sufficiently powerful weird machine. Concretely, Pointer Authentication Codes (PAC) meant I could no longer take the popular direct shortcut to a very powerful weird machine via trivial program counter control and ROP or JOP. Instead I built a remote arbitrary memory read and write primitive which in practise is just as powerful and something which the current implementation of PAC, which focuses almost exclusively on restricting control-flow, wasn't designed to mitigate.
Signed pointers are just a mitigation. With enough time to find other primitives/constructs (from less severe but more common bugs) you will work around them.
AWDL is a wireless protocol that Apple used for things like AirDrop. In the AWDL handling code in the kernel there is a 60-byte buffer that gets copied over by an up-to 1024 byte buffer supplied by an attacker. Using other bugs and poor address randomization Ian Beer from Google Project Zero discloses kernel memory, then constructs a kernel read and write primitive. Then he demonstrates how this can be used to gain privileged code execution in userspace by launching the calculator and making a program to extract user photos.
Are Androids without crapware as insecure as iPhones?
I wonder if the daily HN article about Apple failing to be secure is a result of 1 OS, 1 phone. Where as no one is going to put the effort to find an exploit on a phone with 1% market share.
Android has has several critical flaws recently. The ones I can remember are stagefright and dirtyCoW. Stagefright was easily remotely triggerable since it was in a media library that runs when getting sent media.
The main difference between the two I have seen is ios users get an update that fixes the issue often after their device has stopped getting feature updates while many android users are on kernels that haven't received an update in the last few years.
Stagefright was ~5 years ago now, though. I remember it, because the company I worked for at the time flipped out and banned all Android phones from their network for over a year.
It was fantastic; I got a whole year of not being able to see work emails after I went home, and then they let us opt out of the invasive MDM software that they wanted to put on Android phones to let them access corporate email. All for a bug that my phone wasn't even vulnerable to.
By the time I left, I had gone 4 years without ever responding to unexpected evening emails. And now that I know it's possible, I'm never going back! :)
dirtyCoW was a linux kernel bug. The reason the news and drama was all in the android scene was every desktop/server linux installation would be patched long before a malicious binary would be run on the machine (I think it was patched before the news even broke). Android would be left with the exploit until all of the older devices made it to e-waste. It also meant the rooting efforts would be helped somewhat.
The problem with android is that there is a lot of software in the end OS that's not open source and is delivered as binaries from component manufacturers (the GPU drivers tend to be the worst, they almost universally come from Qualcomm since most phones now use the same series of SoCs.) Once the hardware is released these are rarely updated if ever which means the vulnerabilities aren't patched. The phone manufacturers are just helpless as the community is in this situation. Project treble mitigates this to some degree but the individual software components still can't be updated.
> After a day or so of analysis and reversing I realize that yes, this is in fact another exploitable zero-day in AWDL. This is the third, also reachable in the default configuration of iOS.
> Using two MacOS laptops and enabling AirDrop on both of them I used a kernel debugger to edit the SyncTree TLV sent by one of the laptops, which caused the other one to kernel panic due to an out-of-bounds memmove.
How did this even pass the _smell_ test? How did it get through code reviews and auditing? You're allocating from an untrusted source. It's like memory management 101. I mean, my goodness, it's from a wireless source, at that.
† In this specific scenario, namely the list of `IO80211AWDLPeer`s.