Towards the next generation of XNU memory safety: kalloc_type
security.apple.com
security.apple.com
I can see references to these builtins in the latest xnu source[1], but not in upstream llvm[2], or Apple's forks of llvm[3] and clang[4].
It's been possible to build your own XNU[5] for a long time. Is that still possible now?
[1]: https://github.com/apple-oss-distributions/xnu
[2]: https://github.com/llvm/llvm-project
[3]: https://github.com/apple-oss-distributions/llvmCore
[4]: https://github.com/apple-oss-distributions/clang
[5]: https://kernelshaman.blogspot.com/2021/02/building-xnu-for-macos-112-intel-apple.html #include <stdio.h>
struct foo {
uintptr_t p __attribute__((xnu_usage_semantics("pointer")));
};
int main() {
struct foo f = { 100 };
printf("%lu\n", f.p);
return 0;
}
Doesn't seem -Wxnu-typed-allocators (which I forgot to mention above) is present in my version. Didn't test the others.I also just realized that the latest tagged release of https://github.com/apple-oss-distributions/clang is 800.0.38, my clang is reporting 1400.0.29.102. Is Apple no longer releasing the source for their compilers?
https://support.apple.com/guide/security/memory-safe-iboot-i...
If the latter, wouldn’t that be an obvious risk mitigation without even needing to segment by type (ie only hand out zeroed pages for allocations)?
If they’re being zeroed out, then I’m not sure I understand how grouping by type improves UAF security since the attacker couldn’t control the contents.
I’m sure I’m just ignorant here since there’s so much research into this type of hardening. Genuinely curious.
They give an example in the "Type isolation" section where they have two different types, but one has controllable value, and the other has a pointer. In this scenario the attacker has some way to read the time field. What the attacker does is get you to heap allocate a timespec struct, free it but keep the pointer (classic dangling reference), and then get your code to allocate an iovec. Assuming the attacker has done things "right", the iovec that they allocate will be allocated at the address of the timespec that was freed.
Now, it's reasonable to believe that the attacker has someway to get the time described by a timespec, so that is what the attacker now does, except now the dangling timespec pointer is pointing to what is actually an iovec, and so the tv_sec field is no longer a real time value, but is actually a pointer. So when the attacker reads that time out, they can convert it back into an address in memory, which gives them the ability to bypass address space randomization.
Now, if the attacker has complete control of the tv_sec they can also read arbitrary memory by setting the time on the ostensibly dangling pointer, and then getting the kernel to read values from the iovec.
It should be fairly easy to see how separating where different types of object are stored breaks this path - the most the attacker can do with a dangling timespec is point it at a different timespec, ditto for iovec.
Obviously this is not some magical Security Is Fixed panacea - the underlying use after free bug is the issue - but it does make actually exploiting that UaF harder.
The post references a few other implementations of type segregating allocators, which also have blog posts that may provide some different details (although obviously the core type segregation portions also have a lot of overlap).
Things like pointer authentication, memory tagging, CHERI, aslr, stack cookies, etc, etc are all mitigations to limit what can be done once someone finds a memory error, and all of these things are relatively expensive costs incurred as a result of the lack of safety of the code being written. I am somewhat curious as to whether anyone has done a like vs like benchmark of “real” code with and without these mitigations (and i mean the entire system, libraries, and kernel because otherwise the benchmark picks up those perf hits).
We can then compare that cost to the performance gain you get from pointer nonsense, etc people do for performance vs comparable rust/go/swift code that is memory safe.
Obviously the mitigations can’t be removed (yet?), but I would like to be able to point to some comparison when people start talking about how much slower safe languages are.
Presumably CHERI/MTE/etc. hardware support would help (at the expense of die area and 128-bit pointers); it would be nice to see something like this in Apple Silicon.
MTE consumes huge amounts of memory (if you have an N-bit tag for every M bytes, you burn (total ram / M * N) bits. Imagine you have say a 4 bit tag for every 16 bytes - that works out to 3% of memory being used. But there are additional costs: now to read from a pointer you have the load cost of the pointer itself, but also the tag. Now I assume cpu hardware folk know how this kind of stuff could be faster than the obvious thing I would try, but no matter what happens this ends up using more memory and more processing time - a cost that would be avoided if everything was in a safe language.
CHERI has the same set of problems, but I never looked into it too much as the costs seemed like they’d be huge, and conceptually it’s very “obvious” (it took me a bit of time to understand that MTE is intended to be probabilistic, and how you would then make use of it - cheri is much easier to comprehend). But the same thing happens: you need more memory, and it requires more cpu time. Extra fun is that with cheri the hardware burns resources in bounds checks for pointer accesses, even though in safe languages those have already happened.
Pointer authentication has cpu time costs at least - I don’t think there is meaningful memory overhead, but clearly it has enough cost that simply “authenticate every pointer” isn’t something that can be done otherwise someone would have written a post about doing so.
But this also misses the point: I did not say “software cost”, I said “cost” without qualifiers. The reason is that it doesn’t matter whether any particular mitigation is implemented in software or in hardware, the only reason the mitigation is present is to make code written in unsafe languages less trivially exploitable.
If for the sake of argument someone wrote everything from the kernel to all of userspace solely in memory safe languages, then none of these mitigations would be necessary. So you’d get back the ram, the cpu time, the die space, etc that is all burned solely for the benefit of unsafe languages.
I said, very clearly, that these mitigations are necessary because of code written in unsafe languages, and that these mitigations are not free.
Some basic googling says that on an M7 there are 4 bit tags with a 64byte granularity. Now before any other costs you are burn 0.8% of your memory on these tags. The other costs are along the lines of increased chip complexity (caches, logic, lookahead/ooo etc).
It may be that we're ok with these costs, but that doesn't change the fact that the costs exist, and that the exist largely to mitigate exploits in unsafe code.
The Turing award speech of CAR Hoare about their customers clearly not wanting bounds checking disabled.
Unisys still sells Burroughs (naturally somehow modernized) as ClearPath MCP. Sales pitch, being a mainframe OS for business whose top priority is security.
We know how it goes, it just happened that while most computers weren't a target, no one cared about security beyond a couple of virus when using pirated software.
Hmm, seems like Apple should do better when there are perfectly good languages available which could prevent memory safety issues altogether. Seems like Apple would want to compete with Linux rather than watch it race away into the memory safe future, leaving XNU behind with legacy memory safety problems.
Apple could rewrite it with the same syscall ABI but make it immensely more secure, compartmentalized, and efficient. They won't because of the performance pressures and rewards their employees are under. This is how big companies fail repeatedly and are bested by startups.
Zircon is the closest attempt I’ve seen and it has taken years to be shipped on a single, battery-less device. It might be close to ready, but it was likely expensive for Google, which is now scrambling to rein in R&D spending.
Meanwhile, the incremental security improvements that Apple and Google have made have driven up the cost of zero days sharply on their devices
You misunderstand the approach to migrating: not rip-replace rewrite the world all at once, but refactoring internal areas section-by-section. There's no law that says syscalls have to change.
There are few incentives in corporate land to rewrite but to keep ducktapping together the past without addressing the underlying antique, brittle software engineering underneath.
seL4, Amoeba, Plan 9, MINIX 3, and DragonFly show what's possible. Neptune OS is interesting. Capabilities-secure, DAC, microkernels are the future. Ideally, kernel modules shouldn't exist and everything apart from memory mapping, security, and IPC should be a "userland-ish" process. Any complicated operations involving multiple components should be software transactional across multiple "processes".
Monolithic kernels with file servers and every conceivable library built-in, written in C, with enormous wads of firmware blobs and millions of lines of fragile drivers without any internal protections are the past. Patching together piles of mud is still patching mud.
The long term roadmap is to keep moving the infrastructure to userspace this way, until no one else besides Apple themselves can touch kernel memory space.
A kernel with Apple only code, is more stable and secure than letting everyone to the party.
Every little piece of the puzzle helps.
Going beyond PNP and ACPI, per-OS hardware drivers could be made unnecessary if vendors provided "ACPI"-like APIs that exposed standard, introspectable functions and data structures to the OS in a unified way independent of bus and architecture, i.e., a GPU, a block device, a serial port, a sensor. A universal "IOKit" would required less integration and development effort for all to support, and every OS would get support for free.
Also as other have pointed out, Apple is migrating all kernel extensions into userspace, their long term roadmap is to transition into a micro-kernel like experience, even if not a pure one.