Message ID | 20190605205706.569-7-richard.henderson@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | linux-user/aarch64: Support PROT_BTI | expand |
On Wed, Jun 05, 2019 at 09:57:06PM +0100, Richard Henderson wrote: > This will build with older toolchains, without the upstream support > for -mbranch-protection. Such a toolchain will produce a warning > in such cases, > > ld: warning: /tmp/ccyZt0kq.o: unsupported GNU_PROPERTY_TYPE (5) \ > type: 0xc0000000 > > but the still places the note at the correct location in the binary > for processing by the runtime loader. > > Signed-off-by: Richard Henderson <richard.henderson@linaro.org> > --- > tests/tcg/aarch64/bti-1.c | 77 +++++++++++++++++++++++++++++++ > tests/tcg/aarch64/bti-crt.inc.c | 69 +++++++++++++++++++++++++++ > tests/tcg/aarch64/Makefile.target | 3 ++ > 3 files changed, 149 insertions(+) > create mode 100644 tests/tcg/aarch64/bti-1.c > create mode 100644 tests/tcg/aarch64/bti-crt.inc.c > > diff --git a/tests/tcg/aarch64/bti-1.c b/tests/tcg/aarch64/bti-1.c > new file mode 100644 > index 0000000000..2aee57ea7a > --- /dev/null > +++ b/tests/tcg/aarch64/bti-1.c > @@ -0,0 +1,77 @@ > +/* > + * Branch target identification, basic notskip cases. > + */ > + > +#include "bti-crt.inc.c" > + > +/* > + * Work around lack of -mbranch-protection=standard in older toolchains. > + * The signal handler is invoked by the kernel with PSTATE.BTYPE=2, which > + * means that the handler must begin with a marker like BTI_C. > + */ > +asm("skip2_sigill1:\n\ > + hint #34\n\ > + b skip2_sigill2\n\ > +.type skip2_sigill1,%function\n\ > +.size skip2_sigill1,8"); > + > +extern void skip2_sigill1(int sig, siginfo_t *info, ucontext_t *uc) > + __attribute__((visibility("hidden"))); > + > +static void __attribute__((used)) > +skip2_sigill2(int sig, siginfo_t *info, ucontext_t *uc) > +{ > + uc->uc_mcontext.pc += 8; > + uc->uc_mcontext.pstate = 1; > +} > + > +#define NOP "nop" > +#define BTI_N "hint #32" > +#define BTI_C "hint #34" > +#define BTI_J "hint #36" > +#define BTI_JC "hint #38" > + > +#define BTYPE_1(DEST) \ > + asm("mov %0,#1; adr x16, 1f; br x16; 1: " DEST "; mov %0,#0" \ > + : "=r"(skipped) : : "x16") > + > +#define BTYPE_2(DEST) \ > + asm("mov %0,#1; adr x16, 1f; blr x16; 1: " DEST "; mov %0,#0" \ > + : "=r"(skipped) : : "x16", "x30") > + > +#define BTYPE_3(DEST) \ > + asm("mov %0,#1; adr x15, 1f; br x15; 1: " DEST "; mov %0,#0" \ > + : "=r"(skipped) : : "x15") > + > +#define TEST(WHICH, DEST, EXPECT) \ > + do { WHICH(DEST); fail += skipped ^ EXPECT; } while (0) > + > + > +int main() > +{ > + int fail = 0; > + int skipped; > + > + /* Signal-like with SA_SIGINFO. */ > + signal_info(SIGILL, skip2_sigill1); > + > + TEST(BTYPE_1, NOP, 1); > + TEST(BTYPE_1, BTI_N, 1); > + TEST(BTYPE_1, BTI_C, 0); > + TEST(BTYPE_1, BTI_J, 0); > + TEST(BTYPE_1, BTI_JC, 0); > + > + TEST(BTYPE_2, NOP, 1); > + TEST(BTYPE_2, BTI_N, 1); > + TEST(BTYPE_2, BTI_C, 0); > + TEST(BTYPE_2, BTI_J, 1); > + TEST(BTYPE_2, BTI_JC, 0); > + > + TEST(BTYPE_3, NOP, 1); > + TEST(BTYPE_3, BTI_N, 1); > + TEST(BTYPE_3, BTI_C, 1); > + TEST(BTYPE_3, BTI_J, 0); > + TEST(BTYPE_3, BTI_JC, 0); > + > + return fail; > +} > diff --git a/tests/tcg/aarch64/bti-crt.inc.c b/tests/tcg/aarch64/bti-crt.inc.c > new file mode 100644 > index 0000000000..bb363853de > --- /dev/null > +++ b/tests/tcg/aarch64/bti-crt.inc.c > @@ -0,0 +1,69 @@ > +/* > + * Minimal user-environment for testing BTI. > + * > + * Normal libc is not built with BTI support enabled, and so could > + * generate a BTI TRAP before ever reaching main. > + */ > + > +#include <stdlib.h> > +#include <signal.h> > +#include <ucontext.h> > +#include <asm/unistd.h> > + > +int main(void); > + > +void _start(void) > +{ > + exit(main()); > +} > + > +void exit(int ret) > +{ > + register int x0 __asm__("x0") = ret; > + register int x8 __asm__("x8") = __NR_exit; > + > + asm volatile("svc #0" : : "r"(x0), "r"(x8)); > + __builtin_unreachable(); > +} > + > +/* > + * Irritatingly, the user API struct sigaction does not match the > + * kernel API struct sigaction. So for simplicity, isolate the > + * kernel ABI here, and make this act like signal. > + */ > +void signal_info(int sig, void (*fn)(int, siginfo_t *, ucontext_t *)) > +{ > + struct kernel_sigaction { > + void (*handler)(int, siginfo_t *, ucontext_t *); > + unsigned long flags; > + unsigned long restorer; > + unsigned long mask; > + } sa = { fn, SA_SIGINFO, 0, 0 }; > + > + register int x0 __asm__("x0") = sig; > + register void *x1 __asm__("x1") = &sa; > + register void *x2 __asm__("x2") = 0; > + register int x3 __asm__("x3") = sizeof(unsigned long); > + register int x8 __asm__("x8") = __NR_rt_sigaction; > + > + asm volatile("svc #0" > + : : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x8) : "memory"); > +} > + > +/* > + * Create the PT_NOTE that will enable BTI in the page tables. > + * This will be created by the compiler with -mbranch-protection=standard, > + * but as of 2019-03-29, this is has not been committed to gcc mainline. > + * This will probably be in GCC10. FYI, GCC9 has it. > + */ > +asm(".section .note.gnu.property,\"a\"\n\ > + .align 3\n\ > + .long 4\n\ > + .long 16\n\ > + .long 5\n\ > + .string \"GNU\"\n\ > + .long 0xc0000000\n\ > + .long 4\n\ > + .long 1\n\ > + .align 3\n\ > + .previous"); Note, this won't be enough to generate the PT_GNU_PROPERTY entry in the program header table using older tools. This may be work-round-able with a linker script, but I haven't looked into it. > diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target > index 2bb914975b..21da3bc37f 100644 > --- a/tests/tcg/aarch64/Makefile.target > +++ b/tests/tcg/aarch64/Makefile.target > @@ -18,4 +18,7 @@ run-fcvt: fcvt > AARCH64_TESTS += pauth-1 > run-pauth-%: QEMU += -cpu max > > +AARCH64_TESTS += bti-1 > +bti-1: LDFLAGS += -nostartfiles -nodefaultlibs -nostdlib > + Doesn't -nostdlib imply -nodefaultlibs and -nostartfiles? Cheers ---Dave
On 6/6/19 5:21 AM, Dave Martin wrote: >> +/* >> + * Create the PT_NOTE that will enable BTI in the page tables. >> + * This will be created by the compiler with -mbranch-protection=standard, >> + * but as of 2019-03-29, this is has not been committed to gcc mainline. >> + * This will probably be in GCC10. > > FYI, GCC9 has it. Thanks. >> + */ >> +asm(".section .note.gnu.property,\"a\"\n\ >> + .align 3\n\ >> + .long 4\n\ >> + .long 16\n\ >> + .long 5\n\ >> + .string \"GNU\"\n\ >> + .long 0xc0000000\n\ >> + .long 4\n\ >> + .long 1\n\ >> + .align 3\n\ >> + .previous"); > > Note, this won't be enough to generate the PT_GNU_PROPERTY entry in the > program header table using older tools. > > This may be work-round-able with a linker script, but I haven't looked > into it. Yes, a linker script can create such an entry. >> +AARCH64_TESTS += bti-1 >> +bti-1: LDFLAGS += -nostartfiles -nodefaultlibs -nostdlib >> + > > Doesn't -nostdlib imply -nodefaultlibs and -nostartfiles? I don't believe so, but I'll double-check. r~
diff --git a/tests/tcg/aarch64/bti-1.c b/tests/tcg/aarch64/bti-1.c new file mode 100644 index 0000000000..2aee57ea7a --- /dev/null +++ b/tests/tcg/aarch64/bti-1.c @@ -0,0 +1,77 @@ +/* + * Branch target identification, basic notskip cases. + */ + +#include "bti-crt.inc.c" + +/* + * Work around lack of -mbranch-protection=standard in older toolchains. + * The signal handler is invoked by the kernel with PSTATE.BTYPE=2, which + * means that the handler must begin with a marker like BTI_C. + */ +asm("skip2_sigill1:\n\ + hint #34\n\ + b skip2_sigill2\n\ +.type skip2_sigill1,%function\n\ +.size skip2_sigill1,8"); + +extern void skip2_sigill1(int sig, siginfo_t *info, ucontext_t *uc) + __attribute__((visibility("hidden"))); + +static void __attribute__((used)) +skip2_sigill2(int sig, siginfo_t *info, ucontext_t *uc) +{ + uc->uc_mcontext.pc += 8; + uc->uc_mcontext.pstate = 1; +} + +#define NOP "nop" +#define BTI_N "hint #32" +#define BTI_C "hint #34" +#define BTI_J "hint #36" +#define BTI_JC "hint #38" + +#define BTYPE_1(DEST) \ + asm("mov %0,#1; adr x16, 1f; br x16; 1: " DEST "; mov %0,#0" \ + : "=r"(skipped) : : "x16") + +#define BTYPE_2(DEST) \ + asm("mov %0,#1; adr x16, 1f; blr x16; 1: " DEST "; mov %0,#0" \ + : "=r"(skipped) : : "x16", "x30") + +#define BTYPE_3(DEST) \ + asm("mov %0,#1; adr x15, 1f; br x15; 1: " DEST "; mov %0,#0" \ + : "=r"(skipped) : : "x15") + +#define TEST(WHICH, DEST, EXPECT) \ + do { WHICH(DEST); fail += skipped ^ EXPECT; } while (0) + + +int main() +{ + int fail = 0; + int skipped; + + /* Signal-like with SA_SIGINFO. */ + signal_info(SIGILL, skip2_sigill1); + + TEST(BTYPE_1, NOP, 1); + TEST(BTYPE_1, BTI_N, 1); + TEST(BTYPE_1, BTI_C, 0); + TEST(BTYPE_1, BTI_J, 0); + TEST(BTYPE_1, BTI_JC, 0); + + TEST(BTYPE_2, NOP, 1); + TEST(BTYPE_2, BTI_N, 1); + TEST(BTYPE_2, BTI_C, 0); + TEST(BTYPE_2, BTI_J, 1); + TEST(BTYPE_2, BTI_JC, 0); + + TEST(BTYPE_3, NOP, 1); + TEST(BTYPE_3, BTI_N, 1); + TEST(BTYPE_3, BTI_C, 1); + TEST(BTYPE_3, BTI_J, 0); + TEST(BTYPE_3, BTI_JC, 0); + + return fail; +} diff --git a/tests/tcg/aarch64/bti-crt.inc.c b/tests/tcg/aarch64/bti-crt.inc.c new file mode 100644 index 0000000000..bb363853de --- /dev/null +++ b/tests/tcg/aarch64/bti-crt.inc.c @@ -0,0 +1,69 @@ +/* + * Minimal user-environment for testing BTI. + * + * Normal libc is not built with BTI support enabled, and so could + * generate a BTI TRAP before ever reaching main. + */ + +#include <stdlib.h> +#include <signal.h> +#include <ucontext.h> +#include <asm/unistd.h> + +int main(void); + +void _start(void) +{ + exit(main()); +} + +void exit(int ret) +{ + register int x0 __asm__("x0") = ret; + register int x8 __asm__("x8") = __NR_exit; + + asm volatile("svc #0" : : "r"(x0), "r"(x8)); + __builtin_unreachable(); +} + +/* + * Irritatingly, the user API struct sigaction does not match the + * kernel API struct sigaction. So for simplicity, isolate the + * kernel ABI here, and make this act like signal. + */ +void signal_info(int sig, void (*fn)(int, siginfo_t *, ucontext_t *)) +{ + struct kernel_sigaction { + void (*handler)(int, siginfo_t *, ucontext_t *); + unsigned long flags; + unsigned long restorer; + unsigned long mask; + } sa = { fn, SA_SIGINFO, 0, 0 }; + + register int x0 __asm__("x0") = sig; + register void *x1 __asm__("x1") = &sa; + register void *x2 __asm__("x2") = 0; + register int x3 __asm__("x3") = sizeof(unsigned long); + register int x8 __asm__("x8") = __NR_rt_sigaction; + + asm volatile("svc #0" + : : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x8) : "memory"); +} + +/* + * Create the PT_NOTE that will enable BTI in the page tables. + * This will be created by the compiler with -mbranch-protection=standard, + * but as of 2019-03-29, this is has not been committed to gcc mainline. + * This will probably be in GCC10. + */ +asm(".section .note.gnu.property,\"a\"\n\ + .align 3\n\ + .long 4\n\ + .long 16\n\ + .long 5\n\ + .string \"GNU\"\n\ + .long 0xc0000000\n\ + .long 4\n\ + .long 1\n\ + .align 3\n\ + .previous"); diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 2bb914975b..21da3bc37f 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -18,4 +18,7 @@ run-fcvt: fcvt AARCH64_TESTS += pauth-1 run-pauth-%: QEMU += -cpu max +AARCH64_TESTS += bti-1 +bti-1: LDFLAGS += -nostartfiles -nodefaultlibs -nostdlib + TESTS:=$(AARCH64_TESTS)
This will build with older toolchains, without the upstream support for -mbranch-protection. Such a toolchain will produce a warning in such cases, ld: warning: /tmp/ccyZt0kq.o: unsupported GNU_PROPERTY_TYPE (5) \ type: 0xc0000000 but the still places the note at the correct location in the binary for processing by the runtime loader. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- tests/tcg/aarch64/bti-1.c | 77 +++++++++++++++++++++++++++++++ tests/tcg/aarch64/bti-crt.inc.c | 69 +++++++++++++++++++++++++++ tests/tcg/aarch64/Makefile.target | 3 ++ 3 files changed, 149 insertions(+) create mode 100644 tests/tcg/aarch64/bti-1.c create mode 100644 tests/tcg/aarch64/bti-crt.inc.c