9 ms·
ARM Pointer Authentication
- meditationapp 9y agoHow does using the "unused" bits of a 64-bit pointer differ, functionally, from address space randomization with 64 bits? The search space is the same. Misses are still trivially detectable. By my reading, this allows not a whitelist of pages, but a whitelist of arbitrary addresses. Different granularities entirely. Can anyone else bring a light to bear on this?
- mikeash 9y agoIt looks like this will prevent attacks that use information leakage to figure out valid addresses. With ASLR, you can defeat it if you can get the target to send a return address or other code pointer back to you, because you'll then be able to see where the code is loaded. With this, a pointer to code is likely useless: the authentication code depends on the target address, so you wouldn't be able to forge a valid pointer to a different address, and it depends on the current stack pointer, so you wouldn't even be able to reuse the value to point to the same address unless you found an exploit with the exact same stack depth.
- munin 9y ago> the authentication code depends on the target address The authentication code is a combination of key and context: there are 5 total keys in the system, and then an unlimited number of contexts. Contexts are I think most useable on the "return" edges, because you can add the current stack pointer value to the context when you push the boxed return address onto the stack, then re-derive that context when you're at the return site. That exact scheme doesn't work that well on the forward edge, because the stack pointers will be different when calling function pointer F in function A vs function B. What you can probably do is encode something about the type of F into the context. However, as they outline in the white paper, this isn't enough on its own because the type signature of gets and system are really similar and if your type-to-context encoding scheme maps them to the same context, an attacker could take a call to gets-via-function-pointer and replace that value with the value of system as it appears elsewhere in your program.
- munin 9y agoThe difference is the threat model. ASLR does kind of poorly when the attacker can read and write arbitrary memory, because the attacker can just learn what the addresses of all the objects are by dumping memory, then adjust their attack on-line. If you think that's far-fetched, it isn't, exploits for operating system kernels and web browsers do this. Authenticated pointers can assume this threat model. The attacker can read and write arbitrary memory, but it doesn't do anything for their ability to hijack the control flow of the application because all values stored in memory that relate to control flow are signed and encrypted. The attacker can't create a new code-pointer value and write it in to memory without knowledge of the secret keys, which are not in memory. The attacker could cause the program to crash or exit early, but oh well.
- deleted 9y ago[deleted]
- Taniwha 9y agoI think the deal is that you can't create a good address using the upper bits of a good one ... It's not the misses you worry about, it's the hits
- repiret 9y agoWith address space randomization, if you have a valid pointer to memory A, you can compute a valid pointer to memory B if they are from the same section. You can't do that with this, because the address is part of the signature.
- deleted 9y ago[deleted]
- haberman 9y agoThis doesn't affect regular pointers. It creates a new type of pointer, a "signed pointer." You don't dereference or perform arithmetic on signed pointers. You create signed pointers with one instruction ("PAC") and decode/decrypt them into regular pointers with a different instruction ("AUT"). The decoding/decrypting checks the signature and decodes it into a NULL pointer if the signature is bad. All normal pointers and dereferencing continue to work the same. It only affects code that explicitly decides to use these special instructions to create/validate signed pointers. Only certain code (like the code that saves a return address to the stack) would choose to do this, for cases where an attacker is especially likely to try corrupting the pointer.
- im3w1l 9y ago> The decoding/decrypting checks the signature and decodes it into a NULL pointer if the signature is bad. Hmm wouldn't a trap be better?
- fanf2 9y agoI expect this is to allow for code which might have an uninitialized pointer, and wants to decrypt it concurrently with other validity tests (relying on multiple issue width) Or, the designers didn't want to add an interlock between the memory subsystems that handle traps, and the crypto subsystem that does the decryption.
- colejohnson66 9y agoHow does that not mess with pointer arithmetic?
- Rexxar 9y agoIntuitively, I would have preferred they used a bigger pointer type (96 bits or 128 bits) instead of using unused part of the current pointers that will shrink when will need a bigger address space.
- chrisseaton 9y agoI don't think that's anywhere near a practical concern so it would be over-engineering and wasteful to do that. You'll need entire new processor micro-architectures to use more bits from your 64-bit pointer. It's your hardware address bus that only supports 40- or 48-bits of address space, not your application or operating system. That's already 256 TB as well. I don't think you'll hit that limit any time soon.
- clarry 9y agoI don't know. There are lots of applications for pointer tagging of some sort; and as far as I'm concerned, this authentication code is just another "tag". All the unused bits in a 64-bit pointer are great and plentiful until you realize you might not be the only one wanting to use them. I, for one, would love it if we had hardware support for "fat" pointers with a portion dedicated purely for addressing (possibly with the ability to use the n low bits as tag bits in n-bit aligned access) and another portion dedicated for auxiliary information. Furthermore, there'd need to be some agreement on how these bits are allocated between different actors (user, compiler/jit, OS?). Right now, I would very much like to utilize some of the 64 bits we have. But I'm afraid that 10 years later, my software would be "old and cranky and does crazy shit with pointers so it's not compatible with modern systems and you'd have to rewrite parts of it to get it to run.. good luck".
- floatboth 9y ago"attaches a cryptographic signature to pointer values" I guess everyone who thought that "signed integers" are cryptographically signed weren't THAT wrong after all :D
- yosefk 9y agoThat we seriously discuss using 24 out of 64 pointer bits to prevent one of the many problems with buffer overflow, but we cannot seriously discuss making buffer overflows impossible is very depressing. How about we use 24 bits of data pointers to keep the array size, or 1 bit to indicate "this is a pointer with a size" and 23 bits for the size, and then our load/store with index instructions, as well as freshly added pointer arithmetic instructions, trap when the index exceeds the size? Instead of using bits in instruction pointers to not let one of many kinds of buffer overflow create valid instruction pointers? No good?
- al2o3cr 9y agoHow about we use 24 bits of data pointers to keep the array size, or 1 bit to indicate "this is a pointer with a size" and 23 bits for the size That would imply a pretty big granularity for the sizes - if the maximum size is 4GB, the minimum is 512 bytes. Packing more efficiently might help (for instance, the way segmentation limits on x86 are) but introduces hardware complexity. then our load/store with index instructions, as well as freshly added pointer arithmetic instructions, trap when the index exceeds the size? You've described x86 segmentation pretty clearly here. It's been around since 1978, but most of the mechanism has been disabled in x86-64. Two of the registers involved are still used for things like per-CPU data and stack-smashing protection: http://www.software-architect.net/blog/article/date/2015/03/31/the-gs-segment-and-stack-smashing-protection-1.html http://www.software-architect.net/blog/article/date/2015/03/...
- raverbashing 9y agoHow about we start the retirement of C as it is a liability more than an asset at this day and age How about we only use languages that (as you propose) work with memory slices not naked pointers and where the concept of a null pointer does not exist How about we only operate on memory slices after checking boundaries
- microcolonel 9y agoWhat? Are you going to force them? People write C because it is convenient, productive, and popular enough to attract contributors and find tools. C has the best dynamic analysis and debugging tools of really any language. You can think yourself superiour and turn up your nose, but the fact remains that many people have perfectly good reasons to write and maintain C. Your haughty commentary has no impact on that. You may think that bounds checking is the answer to all situations, but if you're writing a realtime system, there's often no point in running the program if it can fail from an out of bounds read or write anyway. In a flight control system, or an ECU, there is often nothing productive about crashing. You need to verify your pointer logic, instead of hoping your program will crash.