Hacker News new | ask | show | jobs
by amluto 2974 days ago
Someone should make a CPU that does this. Intel CET is a bit of a start, but a CALL instruction that takes a “type” operand and only calls functions of that type would be a big improvement. So would a totally separate return address stack.
3 comments

There is also pointer authentication in ARM: https://lwn.net/Articles/718888/

It seems pretty difficult to define function types at the ISA level, since anything to do with typing is language/VM-specific. What types can arguments have? Are varargs supported? Multiple parameter returns?

Maybe the type would just be an integer that the ABI would assign meaning to. But if you did that, how would the function's type be determined? Some pseudo-instruction at the CALL target? I guess looking at CET it does have some things like this: the ENDBRANCH instructions notate valid indirect branch targets.

> Maybe the type would just be an integer that the ABI would assign meaning to. But if you did that, how would the function's type be determined? Some pseudo-instruction at the CALL target?

Exactly.

What class of errors do you think this would catch that aren't already covered by CET? Simply bolting on another equality check doesn't strike me as all that useful. The type has to live with the code or data, neither one strikes me as easy, both have huge downsides.

And what about CET's shadow stack is deficient compared to a "totally separate return address stack"?

This would catch attempts to use wrong-typed gadgets. With current CET, if I can corrupt a branch target to point to, say, system(), then I win. With type checking, I would need to corrupt an indirect branch that has the right type.

AFAICT the CET shadow stack isn’t protected. An attacker that can write (using a regular write-what-where primitive) could modify the shadow stack. It should have been a new type of memory that is only accessible with special instructions.

AFAICT the CET shadow stack isn’t protected.

Gosh, seems like you could've read all the way to page 6 of the whitepaper: The shadow stack is protected from tamper through the page table protections such that regular store instructions cannot modify the contents of the shadow stack.

It goes on to detail the types of faults that are generated. Any scheme to add further checks should start from a baseline understanding of existing mechanisms, much less chiding on what they should have done. You have to encode the type somewhere, "with type checking" is woefully inadequate. Adding a byte to the opcode? Tagging the function's memory with a preamble? It seems quite difficult to come up with something that doesn't bloat code size and doesn't just marginally increase the attacker difficulty. Plus, why wouldn't an indirect branch have a type?

Oh, you mean all the way to page 135 of the technology preview specification :) Mea culpa!

Anyway, CET already bloats code with ENDBRANCH tags, and you'll find that most fully-software CFI mechanisms (including, IIRC, clang's and grsecurity's) extend their tags to carry some form of hash of the function signature.

No, I literally meant page 6. Section 1.1. That was a direct quote. It's the first page of text that isn't a table of contents. It's one thing to wildly misrepresent the technology as having a giant hole, it's quite another to react to basic evidence of the fact with mawkish drama about "page 135." You didn't even make it through the introduction. The shadow stack is protected from tamper through the page table protections such that regular store instructions cannot modify the contents of the shadow stack. To provide this protection the page table protections are extended to support an additional attribute for pages to mark them as “Shadow Stack” pages. When shadow stacks are enabled, control transfer instructions/flows like near call, far call, call to interrupt/exception handlers, etc. are allowed to store return addresses to the shadow stack. However stores from instructions like MOV, XSAVE, etc. will not be allowed. Likewise control transfer instructions like near ret, far ret, iret, etc. when they attempt to read from the shadow stack the access will fault if the underlying page is not marked as a “Shadow Stack” page. This paging protection detects and prevents conditions that cause an overflow or underflow of the shadow stack or any malicious attempts to redirect the processor to consume data from addresses that are not shadow stack addresses. If you read that and still posted "An attacker that can write (using a regular write-what-where primitive) could modify the shadow stack" just own up to misrepresenting the technology through your poor understanding. Maybe hold off on suggesting improvements until you're better informed.

Okay, so can you now describe your typechecking proposal in light of the ENDBRANCH tag and 0x3E prefix? An indirect branch emitted as a result of a switch() would be tagged with 0x3E and be unavailable to use as a gadget to call system(). Again, you're not impressing me with a full grasp of what you're trying to improve. Just asserting that some vaguely unspecified typechecking would be better than CET and providing example after example that CET actually handles.

LOVE that idea