Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

inlining of stack-protected functions into non-stack-protected functions dangerous #46823

Closed
nickdesaulniers opened this issue Sep 10, 2020 · 11 comments
Labels
bugzilla Issues migrated from bugzilla ipo Interprocedural optimizations

Comments

@nickdesaulniers
Copy link
Member

Bugzilla Link 47479
Resolution FIXED
Resolved on Jan 19, 2021 15:08
Version trunk
OS All
Blocks #4440
CC @dwblaikie,@isanbard,@jyknight,@lalozano,@m-gupta,@marxin,@pcc,@samitolvanen,@stephenhines

Extended Description

Forked from internal google bug b/166163480.

If we have code that is compiled without a stack protector (such as code in the Linux kernel that's trying to restore registers containing the stack canaries), then inlining code that was compiled with stack protector into it can break code. LTO makes this more likely to occur across TUs which may or may not be compiled with the same -fstack-protector flag.

The inliner probably should not inline functions if the caller does not use a stack protector, but the callee does.

Toy example:
// foo.c
#include <alloca.h>
void foo(void *);

void bar(size_t n) {
foo(alloca(n));
}

attribute((no_stack_protector))
void baz(void) {
bar(1024);
}

$ clang -O2 baz.c -c -fstack-protector-strong
$ llvm-objdump -dr baz.o
0000000000000000 :
...
41: c3 retq
42: e8 00 00 00 00 callq 0x47 <bar+0x47>
0000000000000043: R_X86_64_PLT32 __stack_chk_fail-0x4
...
0000000000000050 :
...
8a: c3 retq
8b: e8 00 00 00 00 callq 0x90 <baz+0x40>
000000000000008c: R_X86_64_PLT32 __stack_chk_fail-0x4

Oh, no! bar was compiled with stack protection, was inlined into baz (bad), which now has stack protection, even though we explicitly disabled it for that function.

@nickdesaulniers
Copy link
Member Author

Maybe this bug is in the front end?

$ clang -O2 baz.c -c -fstack-protector-strong -emit-llvm -S

produces bar and baz with the same function attributes: to have a stack protector. So it looks like the front end dropped the attribute((no_stack_protector)), (in IR, it looks like the attribute is called sspstrong.) or rather kept sspstrong?

@nickdesaulniers
Copy link
Member Author

Yeah, what...

// baz2.c
#include <alloca.h>
void foo(void *);

void bar(size_t n) {
foo(alloca(n));
}

attribute((no_stack_protector))
void baz(void) {
bar(1024);
}

attribute((no_stack_protector))
void quux(size_t n) {
foo(alloca(n));
}

$ clang -O2 baz.c -c -fstack-protector-strong -emit-llvm -S

bar -> sspstrong (good)
baz -> sspstrong (bad)
quux -> "" (good)

@nickdesaulniers
Copy link
Member Author

adjustCallerSSPLevel looks fishy.

@nickdesaulniers
Copy link
Member Author

Ah, adjustCallerSSPLevel can't disambiguate between the two cases of:

  1. the caller had no attributes and should be "upgraded" to the inlined callee's stack protection level.
  2. the caller had the C attribute "no_stack_protector" and should not be "upgraded" to the inlined callee's stack protection level, since nothing is left in the IR to show the existence of the C attribute.

Another question is; would we prevent inlining, or prevent "upgrading" the stack protection level? I'd go for avoiding inlining, but then the inliner will face the same ambiguity. I think we need the no_stack_protector C attribute to leave behind a new function level IR attribute. We might need a new EnumAttr in llvm/include/llvm/IR/Attributes.td for "nostackp" to disambiguate between explicitly requesting no stack pointer, vs don't care.

@stephenhines
Copy link
Collaborator

+1 on a new enum value for "nostackp". Indeed there is a tri-state here that isn't being respected.

@nickdesaulniers
Copy link
Member Author

I've started on a patch for this: https://reviews.llvm.org/D87956, which chooses not to inline a stack protected callee into a no stack protector caller.

I think the big question I have is: What should happen if the callee is marked always inline?

@nickdesaulniers
Copy link
Member Author

Landed https://reviews.llvm.org/rGb7926ce6d7a83cdf70c68d82bc3389c04009b841, but there's one more fix I want to do before closing: https://reviews.llvm.org/D87956#2348699.

@nickdesaulniers
Copy link
Member Author

@nickdesaulniers
Copy link
Member Author

Ok, I backed out the previous patches that weren't cleanups, and went with a simpler approach based on feedback: https://reviews.llvm.org/rGbc044a88ee3c16e4164740732a7adc91a29b24f5

@llvmbot
Copy link
Collaborator

llvmbot commented Jan 2, 2021

Cherry-picked commit bc044a8 ("[Inline] prevent inlining on stack protector mismatch") on top of llvmorg-11.0.1-rc2 Git tag helps to fix the issue.

With Nick's baz2.c test-case from comment #​2:

[ Debian clang version 11.0.1-+rc2-1 ]
bar -> sspstrong (good)
baz -> sspstrong (bad)
quux -> "" (good)

[ Selfmade llvmorg-11.0.1-rc2 toolchain + bc044a8 ]
bar -> sspstrong (good)
baz -> "" (good)
quux -> "" (good)

@nickdesaulniers
Copy link
Member Author

Sorry for all of the churn here, but based on https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94722#c9, it seems that GCC is not willing to also make this change.

Based on that, I'm tempted to revert:
commit 7807411 ("[LangRef] Update new ssp/sspstrong/sspreq semantics after D91816")
commit ee571f8 ("[ThinLTO][test] Fix X86/nossp.ll after D91816")
commit bc044a8 ("[Inline] prevent inlining on stack protector mismatch")

and instead send the following Linux kernel patch. Thoughts?

diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c
index db1378c6ff26..baffccce851c 100644
--- a/arch/x86/power/cpu.c
+++ b/arch/x86/power/cpu.c
@@ -191,7 +191,7 @@ static void fix_processor_context(void)

  • The asm code that gets us here will have restored a usable GDT, although
  • it will be pointing to the wrong alias.
    */
    -static void notrace __restore_processor_state(struct saved_context *ctxt)
    +static void noinline notrace __restore_processor_state(struct saved_context *ctxt)
    {
    struct cpuinfo_x86 *c;

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 10, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla ipo Interprocedural optimizations
Projects
None yet
Development

No branches or pull requests

3 participants