// Compiler: // // Run-time: // status: 0 mod libc { #[link(name = "c")] extern "C" { pub fn puts(s: *const u8) -> i32; 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, pub sa_mask: [u32; 32], pub sa_flags: i32, pub sa_restorer: Option, } 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 = (&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 = (&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 }, ); }