|  | // 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 }, | 
|  | ); | 
|  | } |