This is an archive of the discontinued LLVM Phabricator instance.

[AArch64] Support customizing stack protector guard
ClosedPublic

Authored by nickdesaulniers on Apr 20 2021, 6:47 PM.

Details

Summary

Follow up to D88631 but for aarch64; the Linux kernel uses the command
line flags:

  1. -mstack-protector-guard=sysreg
  2. -mstack-protector-guard-reg=sp_el0
  3. -mstack-protector-guard-offset=0

to use the system register sp_el0 for the stack canary, enabling the
kernel to have a unique stack canary per task (like a thread, but not
limited to userspace as the kernel can preempt itself).

Address pr/47341 for aarch64.

Fixes: https://github.com/ClangBuiltLinux/linux/issues/289
Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>

Diff Detail

Event Timeline

nickdesaulniers requested review of this revision.Apr 20 2021, 6:47 PM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptApr 20 2021, 6:47 PM

I still have some todos that I will get to tomorrow, but posting this early for review.

clang/lib/Driver/ToolChains/Clang.cpp
3150

TODO: can we re-use AArch64SysReg::lookupSysRegByName in the frontend?

xiangzhangllvm accepted this revision.Apr 20 2021, 7:36 PM

I didn't find any problem in the main context of the patch, +1 first.

This revision is now accepted and ready to land.Apr 20 2021, 7:36 PM
nickdesaulniers planned changes to this revision.Apr 20 2021, 8:07 PM

Pretty sure I need to do something with non-zero values of -mstack-protector-guard-offset=0.

kristof.beyls added inline comments.Apr 21 2021, 12:39 AM
clang/test/Driver/stack-protector-guard.c
26

I guess you meant to type "-target arm-linux-gnueabi"?

clang/lib/Driver/ToolChains/Clang.cpp
3111

TODO: tls is not supported on aarch64 in GCC.

3126

TODO: GCC treats this as mutually exclusive when OPT_mstack_protector_guard_EQ == global.

  • fix 32b ARM triple, add test for non-zero offset, actually load ptr
This revision is now accepted and ready to land.Apr 23 2021, 4:20 PM
nickdesaulniers planned changes to this revision.Apr 23 2021, 4:20 PM
nickdesaulniers marked an inline comment as done.
nickdesaulniers added inline comments.
llvm/test/CodeGen/AArch64/stack-guard-sysreg-nonzero-offset.ll
1 ↗(On Diff #340192)

This can be combined with the other test by using different CHECK prefixes.

llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1914–1915

@efriedma can you please check that I have these flags correct? I'm not sure that I do. Also, I suspect that I can't allocated a virtual register for the result of the LDR, since this is post-RA?

  • combine tests, clean up Clang drivers slightly
This revision is now accepted and ready to land.Apr 23 2021, 5:40 PM
clang/lib/Driver/ToolChains/Clang.cpp
3126

And it does so inconsistently; it errors for aarch64, but does literally nothing (no warning, no change to codegen) for x86. I don't plan to be bug compatible here.

3150

I don't think so because llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h is under lib/ not include/. Not sure if I should just remove reg validation?

nickdesaulniers added inline comments.
clang/lib/Driver/ToolChains/Clang.cpp
3150

Guidance provided by @echristo and @jyknight was that we should avoid such linkage requirements on Target/, so instead I'll work on adding a helper to clang/lib/Driver/ToolChains/Arch/AArch64.cpp that duplicates logic from AArch64SysReg::lookupSysRegByName.

nickdesaulniers planned changes to this revision.Apr 26 2021, 12:44 PM
clang/lib/Driver/ToolChains/Clang.cpp
3150

It looks like there's ~1000 possible sysregs for aarch64 ATM; do we really want to add all of those to clang?

  • add support for NPOT and negative offsets
This revision is now accepted and ready to land.Apr 26 2021, 2:28 PM
clang/lib/Driver/ToolChains/Clang.cpp
3150

I'm going to post that as a separate commit/review on top of this series, that way it doesn't pollute this code review. This is ready to be reviewed.

DavidSpickett added inline comments.
clang/lib/Driver/ToolChains/Clang.cpp
3117

Shouldn't this also allow "tls"? At least that's what the previous code works out to, I don't know if that actually works on AArch64 or if it just didn't error.

3150

If the number of different registers people actually use with this option is somewhere < 10 I'd just hardcode the names here as needed. (a large amount of those sysregs won't be suitable for this purpose anyway)

DavidSpickett added inline comments.Apr 27 2021, 2:18 AM
clang/test/Driver/stack-protector-guard.c
59

I'm not sure if this is due to your code or the error machinery itself but these errors are strangely written.

I'd expect:

error: invalid value 'tls' in 'mstack-protector-guard='tls', valid arguments to '-mstack-protector-guard=' are:sysreg global'

Maybe it's assuming that there could be multiple values and ',' means that list option treats the value as a list? Or it's not using the right value and the comma is meant to be after the ='' as in my example.

nickdesaulniers marked 3 inline comments as done.
  • prefer err_drv_invalid_value
clang/lib/Driver/ToolChains/Clang.cpp
3117

I don't think so; GCC seems to support tls for x86 but not for aarch64.
https://godbolt.org/z/6WjEPfhT5

3150

Right, I figure that can be a separate decision from the rest of the implementation, I've forked that off in https://reviews.llvm.org/D101327. Whether it lands or not doesn't matter to me.

clang/test/Driver/stack-protector-guard.c
59

Sure, err_drv_invalid_value would be better.

nickdesaulniers marked an inline comment as not done.Apr 27 2021, 11:13 AM
nickdesaulniers added inline comments.
clang/test/Driver/stack-protector-guard.c
59

Ah, err_drv_invalid_value_with_suggestion puts single quotes around the third parameter %2. Let me send a patch cleaning that up, then I'll rebase this on that.

nickdesaulniers marked an inline comment as done.Apr 27 2021, 11:40 AM
DavidSpickett added inline comments.Apr 28 2021, 5:55 AM
clang/include/clang/Basic/CodeGenOptions.h
385

This is now used for TLS on x86 and sysreg on AArch64 so how about:

/// The TLS base register when StackProtectorGuard is "tls", or register
/// used to store the stack canary for "sysreg".
/// On x86 this can be "fs" or "gs".
/// On AArch64 this can only be "sp_el0".
clang/lib/Driver/ToolChains/Clang.cpp
3117

Fair enough, seems unlikely it would work in any case.

llvm/test/CodeGen/AArch64/stack-guard-sysreg.ll
21

What does NPOT stand for here, something to do with the offset not being a multiple of 8 bytes?

nickdesaulniers marked an inline comment as done.
  • rebase, update comment
nickdesaulniers marked an inline comment as done.May 4 2021, 4:40 PM
nickdesaulniers added inline comments.
llvm/test/CodeGen/AArch64/stack-guard-sysreg.ll
21

Non Power Of Two

nickdesaulniers added inline comments.May 4 2021, 5:25 PM
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1914–1915

@efriedma bumping for review; otherwise can you suggest another reviewer?

  • prefer addDef, explicit register kills
nickdesaulniers added inline comments.May 4 2021, 5:51 PM
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1914–1915

In particular, I'm most curious about the use of RegState::Renamable; the result of reading via mrs is a temporary; but this is post-RA, so isn't it too late for further register renaming?

nickdesaulniers added inline comments.May 4 2021, 5:53 PM
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1914–1915

Additionally, if it's a temporary, should it be RegState::Implicit?

DavidSpickett added a subscriber: ostannard.

All my comments have been answered. Perhaps @ostannard can check AArch64InstrInfo.cpp ?

bumping for review

DavidSpickett requested changes to this revision.EditedMay 11 2021, 2:34 AM

This is probably not showing up in review queues as it got accepted accidentally at some point. I'm going to "Request Changes" then I think you'll need to refresh the diff to get it back into active review.
(I don't know of any other way to "give up" an accept revision)

This revision now requires changes to proceed.May 11 2021, 2:34 AM

I don't know a huge amount about stack protectors.

It's worth adding -verify-machineinstrs to the tests, to check the code passes the internal checks.

llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1917

What is StackProtectorGuardOffset, and could is be out of range for the load? That is something that verify-machineinstrs won't currently check for.

1920

Defs come before Uses in machine instructions.

nickdesaulniers marked an inline comment as done.
  • reorder USE/DEF, add -verify-machineinstrs

It's worth adding -verify-machineinstrs to the tests, to check the code passes the internal checks.

Done.

llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1917

It's passed from the front end; it's an offset added to the pointer load. The Linux kernel uses a value in 1100-1200 range. GCC will let you use whatever large value and keep appending adds for constants greater than 32200: https://godbolt.org/z/1xhbEPbKG, but YAGNI.

dmgreen added inline comments.May 11 2021, 2:12 PM
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1917

OK. It might be worth at least issuing a report_fatal_error if it is out of the range of the instructions handled here. Otherwise the error might be non-obvious, it just truncating the immediate during codegen.

llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1917

Indeed, in particular it looks like the range of LDR is 0-32760 which is sufficient for the kernel, but the range for LDUR is only -256-255, which is not.

nickdesaulniers planned changes to this revision.May 11 2021, 4:48 PM
  • support up to +/- 4096 non-multiples of 8. We could get crazy with psuedo instructions and register scavenging, but this is really overkill; the very first case of positive multiple's of 8 < 32670 will be 99% of the kernel's use case.
nickdesaulniers marked 2 inline comments as done.May 14 2021, 5:03 PM
dmgreen accepted this revision.May 17 2021, 3:35 AM

Thanks. This sounds good to me, if David agrees.

llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1909

-> Unknown

1951

It may be possible to do something earlier, where a we add the MOVi32imm earlier before registry allocation and use that as the register for the ldr offset. That would keep it simpler than trying to find scratch registers, and may be optimized better by the pipeline.

DavidSpickett accepted this revision.May 17 2021, 3:53 AM

LGTM with one nit.

llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1916

For all the if/else here I'd include the braces. I know that once the preprocessor is done it'll be a one line if but visually it's confusing.

This revision is now accepted and ready to land.May 17 2021, 3:53 AM
  • braces, typo fix, add comment about additional approaches
nickdesaulniers marked 3 inline comments as done.May 17 2021, 11:40 AM
nickdesaulniers added inline comments.
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
1951

Sure; added that as a comment; hopefully we wont need it, but "never say never (again)."

nickdesaulniers marked an inline comment as done.May 17 2021, 11:40 AM

Appreciate the reviews; ++beers_owed;

This revision was landed with ongoing or failed builds.May 17 2021, 11:49 AM
This revision was automatically updated to reflect the committed changes.