| // Compiler: |
| // |
| // Run-time: |
| // status: 0 |
| |
| mod libc { |
| #[link(name = "c")] |
| extern "C" { |
| pub fn sigaction(signum: i32, act: *const sigaction, oldact: *mut sigaction) -> i32; |
| pub fn mmap(addr: *mut (), len: usize, prot: i32, flags: i32, fd: i32, offset: i64) -> *mut (); |
| pub fn mprotect(addr: *mut (), len: usize, prot: i32) -> i32; |
| } |
| |
| pub const PROT_READ: i32 = 1; |
| pub const PROT_WRITE: i32 = 2; |
| pub const MAP_PRIVATE: i32 = 0x0002; |
| pub const MAP_ANONYMOUS: i32 = 0x0020; |
| pub const MAP_FAILED: *mut u8 = !0 as *mut u8; |
| |
| /// glibc sigaction |
| #[repr(C)] |
| pub struct sigaction { |
| pub sa_sigaction: Option<unsafe extern "C" fn(i32, *mut (), *mut ())>, |
| pub sa_mask: [u32; 32], |
| pub sa_flags: i32, |
| pub sa_restorer: Option<unsafe extern "C" fn()>, |
| } |
| |
| pub const SA_SIGINFO: i32 = 0x00000004; |
| pub const SIGSEGV: i32 = 11; |
| } |
| |
| static mut COUNT: u32 = 0; |
| static mut STORAGE: *mut u8 = core::ptr::null_mut(); |
| const PAGE_SIZE: usize = 1 << 15; |
| |
| fn main() { |
| unsafe { |
| // Register a segfault handler |
| libc::sigaction( |
| libc::SIGSEGV, |
| &libc::sigaction { |
| sa_sigaction: Some(segv_handler), |
| sa_flags: libc::SA_SIGINFO, |
| ..core::mem::zeroed() |
| }, |
| core::ptr::null_mut(), |
| ); |
| |
| STORAGE = libc::mmap( |
| core::ptr::null_mut(), |
| PAGE_SIZE * 2, |
| 0, |
| libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, |
| -1, |
| 0, |
| ).cast(); |
| if STORAGE == libc::MAP_FAILED { |
| panic!("error: mmap failed"); |
| } |
| |
| let p_count = (&raw mut COUNT) as *mut u32; |
| p_count.write_volatile(0); |
| |
| // Trigger segfaults |
| STORAGE.add(0).write_volatile(1); |
| STORAGE.add(PAGE_SIZE).write_volatile(1); |
| STORAGE.add(0).write_volatile(1); |
| STORAGE.add(PAGE_SIZE).write_volatile(1); |
| STORAGE.add(0).write_volatile(1); |
| STORAGE.add(PAGE_SIZE).write_volatile(1); |
| STORAGE.add(0).read_volatile(); |
| STORAGE.add(PAGE_SIZE).read_volatile(); |
| STORAGE.add(0).read_volatile(); |
| STORAGE.add(PAGE_SIZE).read_volatile(); |
| STORAGE.add(0).read_volatile(); |
| STORAGE.add(PAGE_SIZE).read_volatile(); |
| STORAGE.add(0).write_volatile(1); |
| STORAGE.add(PAGE_SIZE).write_volatile(1); |
| |
| // The segfault handler should have been called for every `write_volatile` and |
| // `read_volatile` in `STORAGE`. If the compiler ignores volatility, some of these writes |
| // will be combined, causing a different number of segfaults. |
| // |
| // This `p_count` read is done by a volatile read. If the compiler |
| // ignores volatility, the compiler will speculate that `*p_count` is |
| // unchanged and remove this check, failing the test. |
| if p_count.read_volatile() != 14 { |
| panic!("error: segfault count mismatch: {}", p_count.read_volatile()); |
| } |
| } |
| } |
| |
| unsafe extern "C" fn segv_handler(_: i32, _: *mut (), _: *mut ()) { |
| let p_count = (&raw mut COUNT) as *mut u32; |
| p_count.write_volatile(p_count.read_volatile() + 1); |
| let count = p_count.read_volatile(); |
| |
| // Toggle the protected page so that the handler will be called for |
| // each `write_volatile` |
| libc::mprotect( |
| STORAGE.cast(), |
| PAGE_SIZE, |
| if count % 2 == 1 { libc::PROT_READ | libc::PROT_WRITE } else { 0 }, |
| ); |
| libc::mprotect( |
| STORAGE.add(PAGE_SIZE).cast(), |
| PAGE_SIZE, |
| if count % 2 == 0 { libc::PROT_READ | libc::PROT_WRITE } else { 0 }, |
| ); |
| } |