blob: e355602f2c3f177df647346e90b6c363cde7abfa [file] [log] [blame] [edit]
//@ 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
}