Comment by JdeBP
2 days ago
It's not exploitable. It's an exploit mitigation, in fact. It's not a bug; it's intentional that it works this way. And Nathan Michaels didn't think that if you want to find Theo de Raadt writing on some subject, better try OpenBSD discussion fora, not the LLVM mailing list. (-:
This was put into OpenBSD back in 2017. It's not "trap instructions". It's "trapsleds". The idea is to trap the sleds (a.k.a. slides) of NOP instructions that linkers used to put in between compiled code for alignment, and which could be exploited with "return-oriented programming" where an attacker could cause execution to jump to any address in the padding range (possibly needing the inexactitude because of constraints upon byte substitutions) and slide along all of the NOPs to the target function.
* https://undeadly.org/cgi?action=article;sid=20170622065629
The instructions have to be trap instructions for it to work.
The conditional branch-backward instruction it is is almost as bad as the series of NOPs, since it is still likely to redirect an attacker to functioning code. (If the attacker can clear the mi flag first, these are just NOPs!)
Hence yes, this is a broken exploit mitigation.
And this is where the OpenBSD people will paraphrase Henry Spencer and say that those who do not understand OpenBSD are doomed to reinvent it badly. (Personally, I think that that's putting OpenBSD onto a pedestal. It's no ideal; one gets the same tradeoffs and problems as everywhere else.) In this case, the reinvention for LLVM targetting ARM, that credits seeing this committed to OpenBSD by Theo de Raadt, totally ignored that the original for gas targetting x86 both trapped and jumped.
I intentionally also pointed you to a collection of several critiques of the whole idea, long-since made. (-:
I think you're misunderstanding. 32 bit ARM has TWO instruction encodings. OpenBSD apparently only knows about one. In thumb encoding, the instruction is a branch, not a trap.
3 replies →
Why, in your own words, is the jump supposed to be there? (Keep in mind this code is in between two functions.)
And why, in your own words, is it OK for the jump to be a conditional backwards jump?
So now you're saying this is a bad reinvention?
Your first comment says "it's intentional that it works this way".
[dead]
> It's not exploitable.
The article doesn't say it is.
> It's an exploit mitigation, in fact.
The article made that clear.
> It's not a bug; it's intentional that it works this way.
What is "this way"? Trap or jump? If you're saying a jump is supposed to count as a trap, it's a pretty bad one. It still allows a lot of jumps to the padding to continue and execute valuable code.
[flagged]
It says why it jumps over nops in the middle of a function. No explanation for jumping backwards at the end of a function.
And it replaces the nops with int3. Not another jump. This code keeps stacking d4.
Putting instructions that halt execution in unreachable parts of the code would make sense, but this is just a jump with a fixed offset, which may technically still be exploitable.
If trap instructions are not possible, I would at least try to make it an unconditional jump to create an infinite loop.
> It's not exploitable. It's an exploit mitigation, in fact. It's not a bug; it's intentional that it works this way.
If the instructions are branching instead of trapping (as explained in the article) then it would be exploitable as a ROP gadget and it would be a bug.
The article states that on ARM Thumb, the instruction meant to be interpreted as a trap does not trap but jumps, instead.
[flagged]
You are misunderstanding the purpose of the initial jump in a trap sled. It is to redirect code which expects to flow through the sled past the traps, while leaving the traps for anything else which lands in that range.
The padding the article is talking about lives between functions. It is not meant to be executed, nothing is needed to jump over it. (The unconditional bx lr before it is the return at the end of the function.)
3 replies →
Yes yes, but it's only an exploit mitigation if the bytes encode a mitigating instruction. On 32 bit ARM, they do. In thumb mode[1], they don't. That's interesting enough to be worth a blog post.
[1] For those who don't realize: author is on a Cortex-M processor per the ISA Ref they cite. These devices support *only* thumb instructions. Although as of thumb2, the encoding is now variable-length and there are lots of not-at-all-orthogonal-with-big-ARM 32 bit variants too. It's... not really the same architecture at all, to be honest.
The discussions you linked to are about amd64, where 0xcc is indeed reliably a trap. The OP is about ARM, and it’s about 0xd4d4, not 0xcc.
Your second link suggests that this mitigation is not very helpful these days. I suppose in that light it doesn't really matter if LLVM changes it to a trap instruction or not.