//@ revisions: all strong none missing //@ assembly-output: emit-asm //@ ignore-apple slightly different policy on stack protection of arrays //@ ignore-msvc stack check code uses different function names //@ ignore-nvptx64 stack protector is not supported //@ ignore-wasm32 //@ [all] compile-flags: -Z stack-protector=all //@ [strong] compile-flags: -Z stack-protector=strong //@ [none] compile-flags: -Z stack-protector=none //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled #![crate_type = "lib"] #![allow(internal_features)] #![feature(unsized_fn_params)] extern "C" { fn strcpy(dest: *mut u8, src: *const u8) -> *mut u8; fn printf(fmt: *const u8, ...) -> i32; fn funcall(p: *mut i32); fn funcall2(p: *mut *mut i32); fn funfloat(p: *mut f64); fn funfloat2(p: *mut *mut f64); fn testi_aux() -> f64; fn getp() -> *mut i32; fn dummy(_: ...) -> i32; static STR: [u8; 1]; } extern "C-unwind" { fn except(p: *mut i32); } #[repr(C)] struct Pair { a: i32, b: i32, } #[repr(C)] struct Nest { first: Pair, second: Pair, } // test1: array of [16 x i8] // CHECK-LABEL: test1{{:|\[}} #[no_mangle] pub fn test1(a: *const u8) { let mut buf: [u8; 16] = [0; 16]; unsafe { strcpy(buf.as_mut_ptr(), a); printf(STR.as_ptr(), buf.as_ptr()); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test2: array [4 x i8] // CHECK-LABEL: test2{{:|\[}} #[no_mangle] pub fn test2(a: *const u8) { let mut buf: [u8; 4] = [0; 4]; unsafe { strcpy(buf.as_mut_ptr(), a); printf(STR.as_ptr(), buf.as_ptr()); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test3: no arrays / no nested arrays // CHECK-LABEL: test3{{:|\[}} #[no_mangle] pub fn test3(a: *const u8) { unsafe { printf(STR.as_ptr(), a); } // all: __stack_chk_fail // strong-NOT: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test4: Address-of local taken (j = &a) // CHECK-LABEL: test4{{:|\[}} #[no_mangle] pub unsafe extern "C" fn test4() { let mut a: i32 = 0; let mut j: *mut i32 = std::ptr::null_mut(); let tmp = std::ptr::read_volatile(&a); let tmp2 = tmp.wrapping_add(1); std::ptr::write_volatile(&mut a, tmp2); std::ptr::write_volatile(&mut j, &mut a); // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test5: PtrToInt Cast // CHECK-LABEL: test5{{:|\[}} #[no_mangle] pub fn test5(a: i32) { let ptr_val: usize = &a as *const i32 as usize; unsafe { printf(STR.as_ptr(), ptr_val); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test6: Passing addr-of to function call // CHECK-LABEL: test6{{:|\[}} #[no_mangle] pub fn test6(mut b: i32) { unsafe { funcall(&mut b as *mut i32); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test7: Addr-of in select instruction // CHECK-LABEL: test7{{:|\[}} #[no_mangle] pub fn test7() { let x: f64; unsafe { let call = testi_aux(); x = call; let y: *const f64 = if call > 0.0 { &x as *const f64 } else { std::ptr::null() }; printf(STR.as_ptr(), y); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test8: Addr-of in phi instruction // CHECK-LABEL: test8{{:|\[}} #[no_mangle] pub fn test8() { let mut _x: f64; unsafe { let call = testi_aux(); _x = call; let y: *const f64; if call > 3.14 { let call1 = testi_aux(); _x = call1; y = std::ptr::null(); } else { if call > 1.0 { y = &_x; } else { y = std::ptr::null(); } } printf(STR.as_ptr(), y); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test9: Addr-of struct element(GEP followed by store) // CHECK-LABEL: test9{{:|\[}} #[no_mangle] pub fn test9() { let mut c = Pair { a: 0, b: 0 }; let b: *mut i32; unsafe { let y: *mut i32 = &mut c.b; b = y; printf(STR.as_ptr(), b); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test10: Addr-of struct element, GEP followed by ptrtoint // CHECK-LABEL: test10{{:|\[}} #[no_mangle] pub fn test10() { let mut c = Pair { a: 0, b: 0 }; unsafe { let y: *mut i32 = &mut c.b; let addr: i64 = y as i64; printf(STR.as_ptr(), addr); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test11: Addr-of struct element, GEP followed by callinst // CHECK-LABEL: test11{{:|\[}} #[no_mangle] pub fn test11() { let mut c = Pair { a: 0, b: 0 }; unsafe { let y: *mut i32 = &mut c.b; printf(STR.as_ptr(), y); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test12: Addr-of a local, optimized into a GEP (e.g., &a - 12) // CHECK-LABEL: test12{{:|\[}} #[no_mangle] pub fn test12() { let mut a: i32 = 0; unsafe { let add_ptr = (&mut a as *mut i32).offset(-12); printf(STR.as_ptr(), add_ptr); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test13: Addr-of a local cast to a ptr of a different type // (e.g., int a; ... ; ptr b = &a;) // CHECK-LABEL: test13{{:|\[}} #[no_mangle] pub fn test13() { let mut a: i32 = 0; unsafe { let mut b: *mut i32 = std::ptr::null_mut(); std::ptr::write_volatile(&mut b, &mut a); // avoid ptr b from optimization let tmp = std::ptr::read_volatile(&b); printf(STR.as_ptr(), tmp); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test14: Addr-of a local cast to a ptr of a different type (optimized) // (e.g., int a; ... ; ptr b = &a;) // CHECK-LABEL: test14{{:|\[}} #[no_mangle] pub fn test14() { let a: i32 = 0; unsafe { funfloat((&a as *const i32).cast::() as *mut f64); } // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test15: Addr-of a variable passed into an invoke instruction // CHECK-LABEL: test15{{:|\[}} #[no_mangle] pub unsafe extern "C" fn test15() -> i32 { let mut a: i32 = 0; except(&mut a as *mut i32); 0 // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test16: Addr-of a struct element passed into an invoke instruction // (GEP followed by an invoke) // CHECK-LABEL: test16{{:|\[}} #[no_mangle] pub unsafe extern "C" fn test16() -> i32 { let mut c = Pair { a: 0, b: 0 }; except(&mut c.a as *mut i32); 0 // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test17: Addr-of a pointer // CHECK-LABEL: test17{{:|\[}} #[no_mangle] pub unsafe extern "C" fn test17() { let mut a: *mut i32 = getp(); let mut _b: *mut *mut i32 = std::ptr::null_mut(); std::ptr::write_volatile(&mut _b, &mut a); let tmp = std::ptr::read_volatile(&_b); funcall2(tmp); // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test18: Addr-of a casted pointer // CHECK-LABEL: test18{{:|\[}} #[no_mangle] pub unsafe extern "C" fn test18() { let mut a: *mut i32 = getp(); let mut _b: *mut *mut i32 = std::ptr::null_mut(); std::ptr::write_volatile(&mut _b, &mut a); let tmp = std::ptr::read_volatile(&_b); funfloat2(tmp as *mut *mut f64); // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test19: array of [4 x i32] // CHECK-LABEL: test19{{:|\[}} #[no_mangle] pub unsafe extern "C" fn test19() -> i32 { let a: [i32; 4] = [0; 4]; let _whole = std::ptr::read_volatile(&a as *const _); // avoid array a from optimization std::ptr::read_volatile(&a[0]) // all: __stack_chk_fail // strong: __stack_chk_fail // none-NOT: __stack_chk_fail // missing-NOT: __stack_chk_fail } // test20: Nested structure, no arrays, no address-of expressions // Verify that the resulting gep-of-gep does not incorrectly trigger // a stack protector // CHECK-LABEL: test20{{:|\[}} #[no_mangle] pub unsafe extern "C" fn test20() { let c = Nest { first: Pair { a: 10, b: 11 }, second: Pair { a: 20, b: 21 } }; let whole: Nest = std::ptr::read_volatile(&c); let v: i32 = whole.second.a; printf(STR.as_ptr(), v); // strong-NOT: __stack_chk_fail } // test21: Address-of a structure taken in a function with a loop where // the alloca is an incoming value to a PHI node and a use of that PHI // node is also an incoming value // Verify that the address-of analysis does not get stuck in infinite // recursion when chasing the alloca through the PHI nodes // CHECK-LABEL: test21{{:|\[}} #[no_mangle] pub unsafe extern "C" fn test21() -> i32 { let mut tmp: *mut u8 = std::ptr::null_mut(); let tmp_ptr: *mut *mut u8 = &mut tmp; let tmp1 = dummy(tmp_ptr); let cur = std::ptr::read_volatile(tmp_ptr); let v = (cur as usize as i64) as i32; if v > 0 { let mut phi_ptr: *mut u8 = cur; let mut phi_idx: i64 = 1; let mut phi_acc: i32 = tmp1; loop { let b = std::ptr::read_volatile(phi_ptr as *const u8); let cond = b == 1u8; let plus = phi_acc.wrapping_add(8); let next_acc = if cond { plus } else { phi_acc }; if (phi_idx as i32) == v { dummy(next_acc); break; } let slot = tmp_ptr.add(phi_idx as usize); let next = std::ptr::read_volatile(slot); phi_ptr = next; phi_idx += 1; phi_acc = next_acc; } } else { dummy(tmp1); } 0 // strong: __stack_chk_fail } // CHECK-LABEL: IgnoreIntrinsicTest{{:|\[}} #[no_mangle] pub unsafe extern "C" fn IgnoreIntrinsicTest() -> i32 { let mut x: i32 = 0; std::ptr::write_volatile(&mut x, 1); let y = std::ptr::read_volatile(&x); let result = y.wrapping_mul(42); result // strong-NOT: __stack_chk_fail }