| //@ 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::<f64>() 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 |
| } |