about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/test/assembly/stack-protector/stack-protector-heuristics-effect.rs396
-rw-r--r--src/test/assembly/stack-protector/stack-protector-target-support.rs286
-rw-r--r--src/test/codegen/stack-protector.rs34
-rw-r--r--src/test/ui/abi/stack-protector.rs99
-rw-r--r--src/test/ui/stack-protector/warn-stack-protector-unsupported.all.stderr4
-rw-r--r--src/test/ui/stack-protector/warn-stack-protector-unsupported.basic.stderr4
-rw-r--r--src/test/ui/stack-protector/warn-stack-protector-unsupported.rs19
-rw-r--r--src/test/ui/stack-protector/warn-stack-protector-unsupported.strong.stderr4
8 files changed, 846 insertions, 0 deletions
diff --git a/src/test/assembly/stack-protector/stack-protector-heuristics-effect.rs b/src/test/assembly/stack-protector/stack-protector-heuristics-effect.rs
new file mode 100644
index 00000000000..530326ab743
--- /dev/null
+++ b/src/test/assembly/stack-protector/stack-protector-heuristics-effect.rs
@@ -0,0 +1,396 @@
+// revisions: all strong basic none missing
+// assembly-output: emit-asm
+// ignore-macos slightly different policy on stack protection of arrays
+// ignore-windows stack check code uses different function names
+// ignore-nvptx64 stack protector is not supported
+// [all] compile-flags: -Z stack-protector=all
+// [strong] compile-flags: -Z stack-protector=strong
+// [basic] compile-flags: -Z stack-protector=basic
+// [none] compile-flags: -Z stack-protector=none
+// compile-flags: -C opt-level=2 -Z merge-functions=disabled
+
+#![crate_type = "lib"]
+
+#![allow(incomplete_features)]
+
+#![feature(unsized_locals, unsized_fn_params)]
+
+
+// CHECK-LABEL: emptyfn:
+#[no_mangle]
+pub fn emptyfn() {
+    // all: __stack_chk_fail
+    // strong-NOT: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// CHECK-LABEL: array_char
+#[no_mangle]
+pub fn array_char(f: fn(*const char)) {
+    let a = ['c'; 1];
+    let b = ['d'; 3];
+    let c = ['e'; 15];
+
+    f(&a as *const _);
+    f(&b as *const _);
+    f(&c as *const _);
+
+    // Any type of local array variable leads to stack protection with the
+    // "strong" heuristic. The 'basic' heuristic only adds stack protection to
+    // functions with local array variables of a byte-sized type, however. Since
+    // 'char' is 4 bytes in Rust, this function is not protected by the 'basic'
+    // heuristic
+    //
+    // (This test *also* takes the address of the local stack variables. We
+    // cannot know that this isn't what triggers the `strong` heuristic.
+    // However, the test strategy of passing the address of a stack array to an
+    // external function is sufficient to trigger the `basic` heuristic (see
+    // test `array_u8_large()`). Since the `basic` heuristic only checks for the
+    // presence of stack-local array variables, we can be confident that this
+    // test also captures this part of the `strong` heuristic specification.)
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// CHECK-LABEL: array_u8_1
+#[no_mangle]
+pub fn array_u8_1(f: fn(*const u8)) {
+    let a = [0u8; 1];
+    f(&a as *const _);
+
+    // The 'strong' heuristic adds stack protection to functions with local
+    // array variables regardless of their size.
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// CHECK-LABEL: array_u8_small:
+#[no_mangle]
+pub fn array_u8_small(f: fn(*const u8)) {
+    let a = [0u8; 2];
+    let b = [0u8; 8];
+    f(&a as *const _);
+    f(&b as *const _);
+
+    // Small arrays do not lead to stack protection by the 'basic' heuristic.
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// CHECK-LABEL: array_u8_large:
+#[no_mangle]
+pub fn array_u8_large(f: fn(*const u8)) {
+    let a = [0u8; 9];
+    f(&a as *const _);
+
+    // Since `a` is a byte array with size greater than 8, the basic heuristic
+    // will also protect this function.
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+#[derive(Copy, Clone)]
+pub struct ByteSizedNewtype(u8);
+
+// CHECK-LABEL: array_bytesizednewtype_9:
+#[no_mangle]
+pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
+    let a = [ByteSizedNewtype(0); 9];
+    f(&a as *const _);
+
+    // Since `a` is a byte array in the LLVM output, the basic heuristic will
+    // also protect this function.
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// CHECK-LABEL: local_var_addr_used_indirectly
+#[no_mangle]
+pub fn local_var_addr_used_indirectly(f: fn(bool)) {
+    let a = 5;
+    let a_addr = &a as *const _ as usize;
+    f(a_addr & 0x10 == 0);
+
+    // This function takes the address of a local variable taken. Although this
+    // address is never used as a way to refer to stack memory, the `strong`
+    // heuristic adds stack smash protection. This is also the case in C++:
+    // ```
+    // cat << EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+    // #include <cstdint>
+    // void f(void (*g)(bool)) {
+    //     int32_t x;
+    //     g((reinterpret_cast<uintptr_t>(&x) & 0x10U) == 0);
+    // }
+    // EOF
+    // ```
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+
+// CHECK-LABEL: local_string_addr_taken
+#[no_mangle]
+pub fn local_string_addr_taken(f: fn(&String)) {
+    let x = String::new();
+    f(&x);
+
+    // Taking the address of the local variable `x` leads to stack smash
+    // protection with the `strong` heuristic, but not with the `basic`
+    // heuristic. It does not matter that the reference is not mut.
+    //
+    // An interesting note is that a similar function in C++ *would* be
+    // protected by the `basic` heuristic, because `std::string` has a char
+    // array internally as a small object optimization:
+    // ```
+    // cat <<EOF | clang++ -O2 -fstack-protector -S -x c++ - -o - | grep stack_chk
+    // #include <string>
+    // void f(void (*g)(const std::string&)) {
+    //     std::string x;
+    //     g(x);
+    // }
+    // EOF
+    // ```
+    //
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+pub trait SelfByRef {
+    fn f(&self) -> i32;
+}
+
+impl SelfByRef for i32 {
+    fn f(&self) -> i32 {
+        return self + 1;
+    }
+}
+
+// CHECK-LABEL: local_var_addr_taken_used_locally_only
+#[no_mangle]
+pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32)) {
+    let x = factory();
+    let g = x.f();
+    sink(g);
+
+    // Even though the local variable conceptually has its address taken, as
+    // it's passed by reference to the trait function, the use of the reference
+    // is easily inlined. There is therefore no stack smash protection even with
+    // the `strong` heuristic.
+
+    // all: __stack_chk_fail
+    // strong-NOT: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+pub struct Gigastruct {
+    does: u64,
+    not: u64,
+    have: u64,
+    array: u64,
+    members: u64
+}
+
+// CHECK-LABEL: local_large_var_moved
+#[no_mangle]
+pub fn local_large_var_moved(f: fn(Gigastruct)) {
+    let x = Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 };
+    f(x);
+
+    // Even though the local variable conceptually doesn't have its address
+    // taken, it's so large that the "move" is implemented with a reference to a
+    // stack-local variable in the ABI. Consequently, this function *is*
+    // protected by the `strong` heuristic. This is also the case for
+    // rvalue-references in C++, regardless of struct size:
+    // ```
+    // cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+    // #include <cstdint>
+    // #include <utility>
+    // void f(void (*g)(uint64_t&&)) {
+    //     uint64_t x;
+    //     g(std::move(x));
+    // }
+    // EOF
+    // ```
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// CHECK-LABEL: local_large_var_cloned
+#[no_mangle]
+pub fn local_large_var_cloned(f: fn(Gigastruct)) {
+    f(Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 });
+
+    // A new instance of `Gigastruct` is passed to `f()`, without any apparent
+    // connection to this stack frame. Still, since instances of `Gigastruct`
+    // are sufficiently large, it is allocated in the caller stack frame and
+    // passed as a pointer. As such, this function is *also* protected by the
+    // `strong` heuristic, just like `local_large_var_moved`. This is also the
+    // case for pass-by-value of sufficiently large structs in C++:
+    // ```
+    // cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+    // #include <cstdint>
+    // #include <utility>
+    // struct Gigastruct { uint64_t a, b, c, d, e; };
+    // void f(void (*g)(Gigastruct)) {
+    //     g(Gigastruct{});
+    // }
+    // EOF
+    // ```
+
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+
+extern "C" {
+    // A call to an external `alloca` function is *not* recognized as an
+    // `alloca(3)` operation. This function is a compiler built-in, as the
+    // man page explains. Clang translates it to an LLVM `alloca`
+    // instruction with a count argument, which is also what the LLVM stack
+    // protector heuristics looks for. The man page for `alloca(3)` details
+    // a way to avoid using the compiler built-in: pass a -std=c11
+    // argument, *and* don't include <alloca.h>. Though this leads to an
+    // external alloca() function being called, it doesn't lead to stack
+    // protection being included. It even fails with a linker error
+    // "undefined reference to `alloca'". Example:
+    // ```
+    // cat<<EOF | clang -fstack-protector-strong -x c -std=c11 - -o /dev/null
+    // #include <stdlib.h>
+    // void * alloca(size_t);
+    // void f(void (*g)(void*)) {
+    //     void * p = alloca(10);
+    //     g(p);
+    // }
+    // int main() { return 0; }
+    // EOF
+    // ```
+    // The following tests demonstrate that calls to an external `alloca`
+    // function in Rust also doesn't trigger stack protection.
+
+    fn alloca(size: usize) -> *mut ();
+}
+
+// CHECK-LABEL: alloca_small_compile_time_constant_arg
+#[no_mangle]
+pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
+    f(unsafe { alloca(8) });
+
+    // all: __stack_chk_fail
+    // strong-NOT: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// CHECK-LABEL: alloca_large_compile_time_constant_arg
+#[no_mangle]
+pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
+    f(unsafe { alloca(9) });
+
+    // all: __stack_chk_fail
+    // strong-NOT: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+
+// CHECK-LABEL: alloca_dynamic_arg
+#[no_mangle]
+pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
+    f(unsafe { alloca(n) });
+
+    // all: __stack_chk_fail
+    // strong-NOT: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// The question then is: in what ways can Rust code generate array-`alloca`
+// LLVM instructions? This appears to only be generated by
+// rustc_codegen_ssa::traits::Builder::array_alloca() through
+// rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). FWICT
+// this is support for the "unsized locals" unstable feature:
+// https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html.
+
+
+// CHECK-LABEL: unsized_fn_param
+#[no_mangle]
+pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
+    let n = if l { 1 } else { 2 };
+    f(*Box::<[u8]>::from(&s[0..n])); // slice-copy with Box::from
+
+    // Even though slices are conceptually passed by-value both into this
+    // function and into `f()`, this is implemented with pass-by-reference
+    // using a suitably constructed fat-pointer (as if the functions
+    // accepted &[u8]). This function therefore doesn't need dynamic array
+    // alloca, and is therefore not protected by the `strong` or `basic`
+    // heuristics.
+
+
+    // all: __stack_chk_fail
+    // strong-NOT: __stack_chk_fail
+    // basic-NOT: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
+
+// CHECK-LABEL: unsized_local
+#[no_mangle]
+pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
+    let n = if l { 1 } else { 2 };
+    let mut a: [u8] = *Box::<[u8]>::from(&s[0..n]); // slice-copy with Box::from
+    f(&mut a);
+
+    // This function allocates a slice as a local variable in its stack
+    // frame. Since the size is not a compile-time constant, an array
+    // alloca is required, and the function is protected by both the
+    // `strong` and `basic` heuristic.
+
+    // all: __stack_chk_fail
+    // strong: __stack_chk_fail
+    // basic: __stack_chk_fail
+    // none-NOT: __stack_chk_fail
+    // missing-NOT: __stack_chk_fail
+}
diff --git a/src/test/assembly/stack-protector/stack-protector-target-support.rs b/src/test/assembly/stack-protector/stack-protector-target-support.rs
new file mode 100644
index 00000000000..5ba46d082e1
--- /dev/null
+++ b/src/test/assembly/stack-protector/stack-protector-target-support.rs
@@ -0,0 +1,286 @@
+// Test that stack smash protection code is emitted for all tier1 and tier2
+// targets, with the exception of nvptx64-nvidia-cuda
+//
+// revisions: r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23
+// revisions: r24 r25 r26 r27 r28 r29 r30 r31 r32 r33 r34 r35 r36 r37 r38 r39 r40 r41 r42 r43 r44
+// revisions: r45 r46 r47 r48 r49 r50 r51 r52 r53 r54 r55 r56 r57 r58 r59 r60 r61 r62 r63 r64 r65
+// revisions: r66 r67 r68 r69 r70 r71 r72 r73 r74 r75 r76 r77 r78 r79 r80 r81 r82 r83 r84
+// assembly-output: emit-asm
+// [r1] compile-flags: --target aarch64-unknown-linux-gnu
+// [r1] needs-llvm-components: aarch64
+// [r2] compile-flags: --target i686-pc-windows-gnu
+// [r2] needs-llvm-components: x86
+// [r3] compile-flags: --target i686-pc-windows-msvc
+// [r3] needs-llvm-components: x86
+// [r4] compile-flags: --target i686-unknown-linux-gnu
+// [r4] needs-llvm-components: x86
+// [r5] compile-flags: --target x86_64-apple-darwin
+// [r5] needs-llvm-components: x86
+// [r6] compile-flags: --target x86_64-pc-windows-gnu
+// [r6] needs-llvm-components: x86
+// [r7] compile-flags: --target x86_64-pc-windows-msvc
+// [r7] needs-llvm-components: x86
+// [r8] compile-flags: --target x86_64-unknown-linux-gnu
+// [r8] needs-llvm-components: x86
+// [r9] compile-flags: --target aarch64-apple-darwin
+// [r9] needs-llvm-components: aarch64
+// [r10] compile-flags: --target aarch64-apple-ios
+// [r10] needs-llvm-components: aarch64
+// [r11] compile-flags: --target aarch64-fuchsia
+// [r11] needs-llvm-components: aarch64
+// [r12] compile-flags: --target aarch64-linux-android
+// [r12] needs-llvm-components: aarch64
+// [r13] compile-flags: --target aarch64-pc-windows-msvc
+// [r13] needs-llvm-components: aarch64
+// [r14] compile-flags: --target aarch64-unknown-linux-musl
+// [r14] needs-llvm-components: aarch64
+// [r15] compile-flags: --target aarch64-unknown-none
+// [r15] needs-llvm-components: aarch64
+// [r16] compile-flags: --target aarch64-unknown-none-softfloat
+// [r16] needs-llvm-components: aarch64
+// [r17] compile-flags: --target arm-linux-androideabi
+// [r17] needs-llvm-components: arm
+// [r18] compile-flags: --target arm-unknown-linux-gnueabi
+// [r18] needs-llvm-components: arm
+// [r19] compile-flags: --target arm-unknown-linux-gnueabihf
+// [r19] needs-llvm-components: arm
+// [r20] compile-flags: --target arm-unknown-linux-musleabi
+// [r20] needs-llvm-components: arm
+// [r21] compile-flags: --target arm-unknown-linux-musleabihf
+// [r21] needs-llvm-components: arm
+// [r22] compile-flags: --target armebv7r-none-eabi
+// [r22] needs-llvm-components: arm
+// [r23] compile-flags: --target armebv7r-none-eabihf
+// [r23] needs-llvm-components: arm
+// [r24] compile-flags: --target armv5te-unknown-linux-gnueabi
+// [r24] needs-llvm-components: arm
+// [r25] compile-flags: --target armv5te-unknown-linux-musleabi
+// [r25] needs-llvm-components: arm
+// [r26] compile-flags: --target armv7-linux-androideabi
+// [r26] needs-llvm-components: arm
+// [r27] compile-flags: --target armv7a-none-eabi
+// [r27] needs-llvm-components: arm
+// [r28] compile-flags: --target armv7r-none-eabi
+// [r28] needs-llvm-components: arm
+// [r29] compile-flags: --target armv7r-none-eabihf
+// [r29] needs-llvm-components: arm
+// [r30] compile-flags: --target armv7-unknown-linux-gnueabi
+// [r30] needs-llvm-components: arm
+// [r31] compile-flags: --target armv7-unknown-linux-gnueabihf
+// [r31] needs-llvm-components: arm
+// [r32] compile-flags: --target armv7-unknown-linux-musleabi
+// [r32] needs-llvm-components: arm
+// [r33] compile-flags: --target armv7-unknown-linux-musleabihf
+// [r33] needs-llvm-components: arm
+// [r34] compile-flags: --target asmjs-unknown-emscripten
+// [r34] needs-llvm-components: webassembly
+// [r35] compile-flags: --target i586-pc-windows-msvc
+// [r35] needs-llvm-components: x86
+// [r36] compile-flags: --target i586-unknown-linux-gnu
+// [r36] needs-llvm-components: x86
+// [r37] compile-flags: --target i586-unknown-linux-musl
+// [r37] needs-llvm-components: x86
+// [r38] compile-flags: --target i686-linux-android
+// [r38] needs-llvm-components: x86
+// [r39] compile-flags: --target i686-unknown-freebsd
+// [r39] needs-llvm-components: x86
+// [r40] compile-flags: --target i686-unknown-linux-musl
+// [r40] needs-llvm-components: x86
+// [r41] compile-flags: --target mips-unknown-linux-gnu
+// [r41] needs-llvm-components: mips
+// [r42] compile-flags: --target mips-unknown-linux-musl
+// [r42] needs-llvm-components: mips
+// [r43] compile-flags: --target mips64-unknown-linux-gnuabi64
+// [r43] needs-llvm-components: mips
+// [r44] compile-flags: --target mips64-unknown-linux-muslabi64
+// [r44] needs-llvm-components: mips
+// [r45] compile-flags: --target mips64el-unknown-linux-gnuabi64
+// [r45] needs-llvm-components: mips
+// [r46] compile-flags: --target mips64el-unknown-linux-muslabi64
+// [r46] needs-llvm-components: mips
+// [r47] compile-flags: --target mipsel-unknown-linux-gnu
+// [r47] needs-llvm-components: mips
+// [r48] compile-flags: --target mipsel-unknown-linux-musl
+// [r48] needs-llvm-components: mips
+// [r49] compile-flags: --target nvptx64-nvidia-cuda
+// [r49] needs-llvm-components: nvptx
+// [r50] compile-flags: --target powerpc-unknown-linux-gnu
+// [r50] needs-llvm-components: powerpc
+// [r51] compile-flags: --target powerpc64-unknown-linux-gnu
+// [r51] needs-llvm-components: powerpc
+// [r52] compile-flags: --target powerpc64le-unknown-linux-gnu
+// [r52] needs-llvm-components: powerpc
+// [r53] compile-flags: --target riscv32i-unknown-none-elf
+// [r53] needs-llvm-components: riscv
+// [r54] compile-flags: --target riscv32imac-unknown-none-elf
+// [r54] needs-llvm-components: riscv
+// [r55] compile-flags:--target riscv32imc-unknown-none-elf
+// [r55] needs-llvm-components: riscv
+// [r56] compile-flags:--target riscv64gc-unknown-linux-gnu
+// [r56] needs-llvm-components: riscv
+// [r57] compile-flags:--target riscv64gc-unknown-none-elf
+// [r57] needs-llvm-components: riscv
+// [r58] compile-flags:--target riscv64imac-unknown-none-elf
+// [r58] needs-llvm-components: riscv
+// [r59] compile-flags:--target s390x-unknown-linux-gnu
+// [r59] needs-llvm-components: systemz
+// [r60] compile-flags:--target sparc64-unknown-linux-gnu
+// [r60] needs-llvm-components: sparc
+// [r61] compile-flags:--target sparcv9-sun-solaris
+// [r61] needs-llvm-components: sparc
+// [r62] compile-flags:--target thumbv6m-none-eabi
+// [r62] needs-llvm-components: arm
+// [r63] compile-flags:--target thumbv7em-none-eabi
+// [r63] needs-llvm-components: arm
+// [r64] compile-flags:--target thumbv7em-none-eabihf
+// [r64] needs-llvm-components: arm
+// [r65] compile-flags:--target thumbv7m-none-eabi
+// [r65] needs-llvm-components: arm
+// [r66] compile-flags:--target thumbv7neon-linux-androideabi
+// [r66] needs-llvm-components: arm
+// [r67] compile-flags:--target thumbv7neon-unknown-linux-gnueabihf
+// [r67] needs-llvm-components: arm
+// [r68] compile-flags:--target thumbv8m.base-none-eabi
+// [r68] needs-llvm-components: arm
+// [r69] compile-flags:--target thumbv8m.main-none-eabi
+// [r69] needs-llvm-components: arm
+// [r70] compile-flags:--target thumbv8m.main-none-eabihf
+// [r70] needs-llvm-components: arm
+// [r71] compile-flags:--target wasm32-unknown-emscripten
+// [r71] needs-llvm-components: webassembly
+// [r72] compile-flags:--target wasm32-unknown-unknown
+// [r72] needs-llvm-components: webassembly
+// [r73] compile-flags:--target wasm32-wasi
+// [r73] needs-llvm-components: webassembly
+// [r74] compile-flags:--target x86_64-apple-ios
+// [r74] needs-llvm-components: x86
+// [r75] compile-flags:--target x86_64-fortanix-unknown-sgx
+// [r75] needs-llvm-components: x86
+// [r75] min-llvm-version: 11.0.0
+// [r76] compile-flags:--target x86_64-fuchsia
+// [r76] needs-llvm-components: x86
+// [r77] compile-flags:--target x86_64-linux-android
+// [r77] needs-llvm-components: x86
+// [r78] compile-flags:--target x86_64-sun-solaris
+// [r78] needs-llvm-components: x86
+// [r79] compile-flags:--target x86_64-unknown-freebsd
+// [r79] needs-llvm-components: x86
+// [r80] compile-flags:--target x86_64-unknown-illumos
+// [r80] needs-llvm-components: x86
+// [r81] compile-flags:--target x86_64-unknown-linux-gnux32
+// [r81] needs-llvm-components: x86
+// [r82] compile-flags:--target x86_64-unknown-linux-musl
+// [r82] needs-llvm-components: x86
+// [r83] compile-flags:--target x86_64-unknown-netbsd
+// [r83] needs-llvm-components: x86
+// [r84] compile-flags: --target x86_64-unknown-redox
+// [r84] needs-llvm-components: x86
+// compile-flags: -Z stack-protector=all
+// compile-flags: -C opt-level=2
+
+#![crate_type = "lib"]
+
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+#[no_mangle]
+pub fn foo() {
+    // CHECK: foo{{:|()}}
+
+    // MSVC does the stack checking within a stack-check function:
+    // r3: calll @__security_check_cookie
+    // r7: callq __security_check_cookie
+    // r13: bl __security_check_cookie
+    // r35: calll @__security_check_cookie
+
+    // cuda doesn't support stack-smash protection
+    // r49-NOT: __security_check_cookie
+    // r49-NOT: __stack_chk_fail
+
+    // Other targets do stack checking within the function, and call a failure function on error
+    // r1: __stack_chk_fail
+    // r2: __stack_chk_fail
+    // r4: __stack_chk_fail
+    // r5: __stack_chk_fail
+    // r6: __stack_chk_fail
+    // r8: __stack_chk_fail
+    // r9: __stack_chk_fail
+    // r10: __stack_chk_fail
+    // r11: __stack_chk_fail
+    // r12: __stack_chk_fail
+    // r14: __stack_chk_fail
+    // r15: __stack_chk_fail
+    // r16: __stack_chk_fail
+    // r17: __stack_chk_fail
+    // r18: __stack_chk_fail
+    // r19: __stack_chk_fail
+    // r20: __stack_chk_fail
+    // r21: __stack_chk_fail
+    // r22: __stack_chk_fail
+    // r23: __stack_chk_fail
+    // r24: __stack_chk_fail
+    // r25: __stack_chk_fail
+    // r26: __stack_chk_fail
+    // r27: __stack_chk_fail
+    // r28: __stack_chk_fail
+    // r29: __stack_chk_fail
+    // r30: __stack_chk_fail
+    // r31: __stack_chk_fail
+    // r32: __stack_chk_fail
+    // r33: __stack_chk_fail
+    // r34: __stack_chk_fail
+    // r36: __stack_chk_fail
+    // r37: __stack_chk_fail
+    // r38: __stack_chk_fail
+    // r39: __stack_chk_fail
+    // r40: __stack_chk_fail
+    // r41: __stack_chk_fail
+    // r42: __stack_chk_fail
+    // r43: __stack_chk_fail
+    // r44: __stack_chk_fail
+    // r45: __stack_chk_fail
+    // r46: __stack_chk_fail
+    // r47: __stack_chk_fail
+    // r48: __stack_chk_fail
+    // r50: __stack_chk_fail
+    // r51: __stack_chk_fail
+    // r52: __stack_chk_fail
+    // r53: __stack_chk_fail
+    // r54: __stack_chk_fail
+    // r55: __stack_chk_fail
+    // r56: __stack_chk_fail
+    // r57: __stack_chk_fail
+    // r58: __stack_chk_fail
+    // r59: __stack_chk_fail
+    // r60: __stack_chk_fail
+    // r61: __stack_chk_fail
+    // r62: __stack_chk_fail
+    // r63: __stack_chk_fail
+    // r64: __stack_chk_fail
+    // r65: __stack_chk_fail
+    // r66: __stack_chk_fail
+    // r67: __stack_chk_fail
+    // r68: __stack_chk_fail
+    // r69: __stack_chk_fail
+    // r70: __stack_chk_fail
+    // r71: __stack_chk_fail
+    // r72: __stack_chk_fail
+    // r73: __stack_chk_fail
+    // r74: __stack_chk_fail
+    // r75: __stack_chk_fail
+    // r76: __stack_chk_fail
+    // r77: __stack_chk_fail
+    // r78: __stack_chk_fail
+    // r79: __stack_chk_fail
+    // r80: __stack_chk_fail
+    // r81: __stack_chk_fail
+    // r82: __stack_chk_fail
+    // r83: __stack_chk_fail
+    // r84: __stack_chk_fail
+}
diff --git a/src/test/codegen/stack-protector.rs b/src/test/codegen/stack-protector.rs
new file mode 100644
index 00000000000..a24e6f1e4f1
--- /dev/null
+++ b/src/test/codegen/stack-protector.rs
@@ -0,0 +1,34 @@
+// revisions: all strong basic none
+// ignore-nvptx64 stack protector not supported
+// [all] compile-flags: -Z stack-protector=all
+// [strong] compile-flags: -Z stack-protector=strong
+// [basic] compile-flags: -Z stack-protector=basic
+
+#![crate_type = "lib"]
+
+#[no_mangle]
+pub fn foo() {
+    // CHECK: @foo() unnamed_addr #0
+
+    // all-NOT: attributes #0 = { {{.*}} sspstrong {{.*}} }
+    // all-NOT: attributes #0 = { {{.*}} ssp {{.*}} }
+    // all: attributes #0 = { {{.*}} sspreq {{.*}} }
+    // all-NOT: attributes #0 = { {{.*}} sspstrong {{.*}} }
+    // all-NOT: attributes #0 = { {{.*}} ssp {{.*}} }
+
+    // strong-NOT: attributes #0 = { {{.*}} sspreq {{.*}} }
+    // strong-NOT: attributes #0 = { {{.*}} ssp {{.*}} }
+    // strong: attributes #0 = { {{.*}} sspstrong {{.*}} }
+    // strong-NOT: attributes #0 = { {{.*}} sspreq {{.*}} }
+    // strong-NOT: attributes #0 = { {{.*}} ssp {{.*}} }
+
+    // basic-NOT: attributes #0 = { {{.*}} sspreq {{.*}} }
+    // basic-NOT: attributes #0 = { {{.*}} sspstrong {{.*}} }
+    // basic: attributes #0 = { {{.*}} ssp {{.*}} }
+    // basic-NOT: attributes #0 = { {{.*}} sspreq {{.*}} }
+    // basic-NOT: attributes #0 = { {{.*}} sspstrong {{.*}} }
+
+    // none-NOT: attributes #0 = { {{.*}} sspreq {{.*}} }
+    // none-NOT: attributes #0 = { {{.*}} sspstrong {{.*}} }
+    // none-NOT: attributes #0 = { {{.*}} ssp {{.*}} }
+}
diff --git a/src/test/ui/abi/stack-protector.rs b/src/test/ui/abi/stack-protector.rs
new file mode 100644
index 00000000000..24bd2e21943
--- /dev/null
+++ b/src/test/ui/abi/stack-protector.rs
@@ -0,0 +1,99 @@
+// run-pass
+// only-x86_64-unknown-linux-gnu
+// revisions: ssp no-ssp
+// [ssp] compile-flags: -Z stack-protector=all
+// compile-flags: -C opt-level=2
+// compile-flags: -g
+
+use std::env;
+use std::process::{Command, ExitStatus};
+
+fn main() {
+    if env::args().len() == 1 {
+        // The test is initially run without arguments. Start the process again,
+        // this time *with* an argument; in this configuration, the test program
+        // will deliberately smash the stack.
+        let cur_argv0 = env::current_exe().unwrap();
+        let mut child = Command::new(&cur_argv0);
+        child.arg("stacksmash");
+
+        if cfg!(ssp) {
+            assert_stack_smash_prevented(&mut child);
+        } else {
+            assert_stack_smashed(&mut child);
+        }
+    } else {
+        vulnerable_function();
+        // If we return here the test is broken: it should either have called
+        // malicious_code() which terminates the process, or be caught by the
+        // stack check which also terminates the process.
+        panic!("TEST BUG: stack smash unsuccessful");
+    }
+}
+
+// Avoid inlining to make sure the return address is pushed to stack.
+#[inline(never)]
+fn vulnerable_function() {
+    let mut x = 5usize;
+    let stackaddr = &mut x as *mut usize;
+    let bad_code_ptr = malicious_code as usize;
+    // Overwrite the on-stack return address with the address of `malicious_code()`,
+    // thereby jumping to that function when returning from `vulnerable_function()`.
+    unsafe { fill(stackaddr, bad_code_ptr, 20); }
+}
+
+// Use an uninlined function with its own stack frame to make sure that we don't
+// clobber e.g. the counter or address local variable.
+#[inline(never)]
+unsafe fn fill(addr: *mut usize, val: usize, count: usize) {
+    let mut addr = addr;
+    for _ in 0..count {
+        *addr = val;
+        addr = addr.add(1);
+    }
+}
+
+// We jump to malicious_code() having wreaked havoc with the previous stack
+// frame and not setting up a new one. This function is therefore constrained,
+// e.g. both println!() and std::process::exit() segfaults if called. We
+// therefore keep the amount of work to a minimum by calling POSIX functions
+// directly.
+// The function is un-inlined just to make it possible to set a breakpoint here.
+#[inline(never)]
+fn malicious_code() {
+    let msg = [112u8, 119u8, 110u8, 101u8, 100u8, 33u8, 0u8]; // "pwned!\0" ascii
+    unsafe {
+        write(1, &msg as *const u8, msg.len());
+        _exit(0);
+    }
+}
+extern "C" {
+    fn write(fd: i32, buf: *const u8, count: usize) -> isize;
+    fn _exit(status: i32) -> !;
+}
+
+
+fn assert_stack_smash_prevented(cmd: &mut Command) {
+    let (status, stdout, stderr) = run(cmd);
+    assert!(!status.success());
+    assert!(stdout.is_empty());
+    assert!(stderr.contains("stack smashing detected"));
+}
+
+fn assert_stack_smashed(cmd: &mut Command) {
+    let (status, stdout, stderr) = run(cmd);
+    assert!(status.success());
+    assert!(stdout.contains("pwned!"));
+    assert!(stderr.is_empty());
+}
+
+
+fn run(cmd: &mut Command) -> (ExitStatus, String, String) {
+    let output = cmd.output().unwrap();
+    let stdout = String::from_utf8_lossy(&output.stdout);
+    let stderr = String::from_utf8_lossy(&output.stderr);
+    println!("status: {}", output.status);
+    println!("stdout: {}", stdout);
+    println!("stderr: {}", stderr);
+    (output.status, stdout.to_string(), stderr.to_string())
+}
diff --git a/src/test/ui/stack-protector/warn-stack-protector-unsupported.all.stderr b/src/test/ui/stack-protector/warn-stack-protector-unsupported.all.stderr
new file mode 100644
index 00000000000..54887715523
--- /dev/null
+++ b/src/test/ui/stack-protector/warn-stack-protector-unsupported.all.stderr
@@ -0,0 +1,4 @@
+warning: `-Z stack-protector=all` is not supported for target nvptx64-nvidia-cuda and will be ignored
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/stack-protector/warn-stack-protector-unsupported.basic.stderr b/src/test/ui/stack-protector/warn-stack-protector-unsupported.basic.stderr
new file mode 100644
index 00000000000..f7a1ee39fb9
--- /dev/null
+++ b/src/test/ui/stack-protector/warn-stack-protector-unsupported.basic.stderr
@@ -0,0 +1,4 @@
+warning: `-Z stack-protector=basic` is not supported for target nvptx64-nvidia-cuda and will be ignored
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/stack-protector/warn-stack-protector-unsupported.rs b/src/test/ui/stack-protector/warn-stack-protector-unsupported.rs
new file mode 100644
index 00000000000..6df5d3cd5ae
--- /dev/null
+++ b/src/test/ui/stack-protector/warn-stack-protector-unsupported.rs
@@ -0,0 +1,19 @@
+// build-pass
+// revisions: all strong basic
+// compile-flags: --target nvptx64-nvidia-cuda
+// needs-llvm-components: nvptx
+// [all] compile-flags: -Z stack-protector=all
+// [strong] compile-flags: -Z stack-protector=strong
+// [basic] compile-flags: -Z stack-protector=basic
+
+#![crate_type = "lib"]
+#![feature(no_core, lang_items)]
+#![no_std]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+pub fn main(){}
diff --git a/src/test/ui/stack-protector/warn-stack-protector-unsupported.strong.stderr b/src/test/ui/stack-protector/warn-stack-protector-unsupported.strong.stderr
new file mode 100644
index 00000000000..ccc2f9f2cc5
--- /dev/null
+++ b/src/test/ui/stack-protector/warn-stack-protector-unsupported.strong.stderr
@@ -0,0 +1,4 @@
+warning: `-Z stack-protector=strong` is not supported for target nvptx64-nvidia-cuda and will be ignored
+
+warning: 1 warning emitted
+