Page MenuHomePhabricator

[safestack] Add canary to unsafe stack frames
ClosedPublic

Authored by eugenis on Apr 6 2016, 4:11 PM.

Details

Reviewers
ed
pcc
timshen
Summary

Add StackProtector to SafeStack.
This adds limited protection against data corruption in the caller frame.
Current implementation treats all stack protector levels as -fstack-protector-all.

This depends on a clang change to allow both safestack and ssp* attributes on the same function.

Diff Detail

Repository
rL LLVM

Event Timeline

eugenis updated this revision to Diff 52863.Apr 6 2016, 4:11 PM
eugenis retitled this revision from to [safestack] Add canary to unsafe stack frames.
eugenis updated this object.
eugenis added reviewers: pcc, ed.
eugenis set the repository for this revision to rL LLVM.
eugenis added a subscriber: llvm-commits.
ed edited edge metadata.Apr 6 2016, 10:35 PM

Thanks for working on this! I know too little of LLVM internals to review this change properly, but I'd be more than happy to run our C library test suite against this. Could you also send out the patch for Clang you mentioned, so I can test this?

I've uploaded the clang part in http://reviews.llvm.org/D18871

pcc added a reviewer: timshen.Apr 7 2016, 3:08 PM

Tim might also want to look at this since he's been working on the stack protector.

lib/CodeGen/SafeStack.cpp
724

Please make this a local variable, or just do the attribute test in the one place you need it.

779

This line doesn't seem necessary.

test/CodeGen/X86/safestack_ssp.ll
2

Why is this a backend test rather than an opt test?

eugenis updated this revision to Diff 52970.Apr 7 2016, 4:13 PM
eugenis edited edge metadata.
eugenis marked 2 inline comments as done.
eugenis added inline comments.
test/CodeGen/X86/safestack_ssp.ll
2

Do you want me to remove it? I think it is useful as-is. I've added more opt tests.

pcc added inline comments.Apr 7 2016, 4:50 PM
test/CodeGen/X86/safestack_ssp.ll
2

Well, I mainly wanted you to tell me why you made this a backend test :) We don't normally test passes this way.

Anyway, if you're just trying to provide test coverage for the codegen pipeline, it looks like the existing test test/CodeGen/X86/safestack.ll is already covering that, so unless you have another reason to keep this test, it seems redundant with the opt tests you added.

eugenis added inline comments.Apr 7 2016, 4:55 PM
test/CodeGen/X86/safestack_ssp.ll
2

test/CodeGen/X86/safestack.ll does not cover the stack protector cookie code.

No other tests verify that StackProtector and SafeStack passes do not apply to the same function.

pcc accepted this revision.Apr 7 2016, 5:06 PM
pcc edited edge metadata.

LGTM

test/CodeGen/X86/safestack_ssp.ll
2

Okay, seems reasonable enough to me. Please add a comment explaining why this is a backend test.

This revision is now accepted and ready to land.Apr 7 2016, 5:06 PM
timshen added inline comments.Apr 8 2016, 1:34 PM
lib/CodeGen/SafeStack.cpp
399

Is this going to work correctly, for say, PowerPC? As of my knowledge on powerpc64le-linux-gnu, there is no "__stack_chk_guard", but similarly has a data member in TCB.

On SSP side I'm actively working on supporting PowerPC - more generally, allowing backends to lower LOAD_STACK_GUARD node manually.

504

OpenBSD doesn't have __stack_chk_fail. It has StackProtector::CreateFailBB.

I wonder if it's easy to share some code between SSP and safestack, though I have no idea what safestack is doing.

timshen added inline comments.Apr 8 2016, 1:44 PM
lib/CodeGen/SafeStack.cpp
399

To be specific, powerpc64le-linux-gnu is similar to AArch64, which has a stack guard as data member in TCB.

504

s/It has StackProtector::CreateFailBB/See StackProtector::CreateFailBB/.

eugenis added inline comments.Apr 8 2016, 2:02 PM
lib/CodeGen/SafeStack.cpp
504

SafeStack maintains a second stack, with the stack pointer either in a thread-local variable or a fixed TLS slot, and moves some locals to that stack.

Anything that may overflow is on the second stack. StackProtector + SafeStack should apply StackProtector cookies to that second stack, and not to the system stack.

It would be great to share more code between the two passes.

Would it be possible to extend the SDAG stuff to handle this case?

eugenis added inline comments.Apr 8 2016, 2:03 PM
lib/CodeGen/SafeStack.cpp
399

Then it should implement getStackCookieLocation - or is it only supported in the SelectionDAG code pass now?

timshen added inline comments.Apr 8 2016, 2:23 PM
lib/CodeGen/SafeStack.cpp
399

Yes, the plan is to only support it in SelectionDAG, because SelectionDAG can pick up tail call optimization (See StackProtectorDescriptor's comment).

Without SafeStack in mind, I was trying to minimize the use of IR approach, since the only reason we need to keep IR form is FastISel.

504

Thanks for the explaining!

It sounds like you want to teach StackProtector to allocate the canary in a different place (aka the second stack). It's currently created in StackProtector::CreatePrologue. Is it possible to call moveStaticAllocasToUnsafeStack to move the slot to the second stack?

moveStaticAllocasToUnsafeStack must be called once per function.

Also, there may be no unsafe stack frame at all, then there would be no need for the guard at all. SafeStack is currently smarter about this than StackProtector; it uses SCEV to prove that allocas can never overflow.

timshen edited edge metadata.Apr 8 2016, 3:02 PM

moveStaticAllocasToUnsafeStack must be called once per function.

As discussed, you may want to call moveStaticAllocasToUnsafeStack, then possibly in SDAG Intrinsic::stackprotector handling you need to handle non-Alloca cases explicitly.

Or if you think there are reasons to keep the code a little bit duplicated between SSP and SafeStack, I'm fine with it, since I'm not the owner of either. :)

Also, there may be no unsafe stack frame at all, then there would be no need for the guard at all. SafeStack is currently smarter about this than StackProtector; it uses SCEV to prove that allocas can never overflow.

What if the source code does overflow? Does SafeStack just fails to build (seems unlikely?), or SSP still needs to be inserted?

I'm fine with it, since I'm not the owner of either. :)

I don't mean that I don't care about the code quality, but to show the lack of domain knowledge of SafeStack. :P

ed accepted this revision.Apr 9 2016, 12:15 PM
ed edited edge metadata.

I've just built a copy of LLVM and Clang with these patches applied. With that compiler I did a full rebuild of all core CloudABI libraries, including the C library's unit test suite (~1000 tests), with -fstack-protector-all and SafeStack enabled. All unit tests still seem to pass.

Also, there may be no unsafe stack frame at all, then there would be no need for the guard at all. SafeStack is currently smarter about this than StackProtector; it uses SCEV to prove that allocas can never overflow.

What if the source code does overflow? Does SafeStack just fails to build (seems unlikely?), or SSP still needs to be inserted?

The unsafe frame consists of allocas that SafeStack could _not_ prove to not overflow. SSP is only needed such allocas exist.

I've looked into reducing code duplication, but it seems to make things a lot more complex. Basically, we could allow StackProtector to insert the cookie and then just move it to the unsafe stack along with other allocas, making sure that it ends up at the bottom of the frame. The problem is, StackProtector does not know if the cookie is needed or not - it needs to ask SafeStack for that. I've considered splitting SafeStack into an analysis pass that would answer this question, and an instrumentation pass, but that seems to add a lot of complexity and a few non-obvious inter-pass dependencies.

I'm inclined to commit this as is, and maybe revisit this point if code duplication becomes a problem.

eugenis closed this revision.Apr 11 2016, 3:34 PM

Thanks for the review, r266004