Skip to content

Commit f14161e

Browse files
yvtantoyo
authored andcommitted
Add a test for volatile loads and stores
1 parent 764770d commit f14161e

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

tests/run/volatile2.rs

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Compiler:
2+
//
3+
// Run-time:
4+
// status: 0
5+
6+
#![no_std]
7+
#![feature(core_intrinsics, start)]
8+
9+
#[panic_handler]
10+
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
11+
core::intrinsics::abort();
12+
}
13+
14+
mod libc {
15+
#[link(name = "c")]
16+
extern "C" {
17+
pub fn puts(s: *const u8) -> i32;
18+
19+
pub fn sigaction(signum: i32, act: *const sigaction, oldact: *mut sigaction) -> i32;
20+
pub fn mmap(addr: *mut (), len: usize, prot: i32, flags: i32, fd: i32, offset: i64) -> *mut ();
21+
pub fn mprotect(addr: *mut (), len: usize, prot: i32) -> i32;
22+
}
23+
24+
pub const PROT_READ: i32 = 1;
25+
pub const PROT_WRITE: i32 = 2;
26+
pub const MAP_PRIVATE: i32 = 0x0002;
27+
pub const MAP_ANONYMOUS: i32 = 0x0020;
28+
pub const MAP_FAILED: *mut u8 = !0 as *mut u8;
29+
30+
/// glibc sigaction
31+
#[repr(C)]
32+
pub struct sigaction {
33+
pub sa_sigaction: Option<unsafe extern "C" fn(i32, *mut (), *mut ())>,
34+
pub sa_mask: [u32; 32],
35+
pub sa_flags: i32,
36+
pub sa_restorer: Option<unsafe extern "C" fn()>,
37+
}
38+
39+
pub const SA_SIGINFO: i32 = 0x00000004;
40+
pub const SIGSEGV: i32 = 11;
41+
}
42+
43+
static mut COUNT: u32 = 0;
44+
static mut STORAGE: *mut u8 = core::ptr::null_mut();
45+
const PAGE_SIZE: usize = 1 << 15;
46+
47+
#[start]
48+
fn main(_argc: isize, _argv: *const *const u8) -> isize {
49+
unsafe {
50+
// Register a segfault handler
51+
libc::sigaction(
52+
libc::SIGSEGV,
53+
&libc::sigaction {
54+
sa_sigaction: Some(segv_handler),
55+
sa_flags: libc::SA_SIGINFO,
56+
..core::mem::zeroed()
57+
},
58+
core::ptr::null_mut(),
59+
);
60+
61+
STORAGE = libc::mmap(
62+
core::ptr::null_mut(),
63+
PAGE_SIZE * 2,
64+
0,
65+
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
66+
-1,
67+
0,
68+
).cast();
69+
if STORAGE == libc::MAP_FAILED {
70+
libc::puts(b"error: mmap failed\0".as_ptr());
71+
return 1;
72+
}
73+
74+
let p_count = (&mut COUNT) as *mut u32;
75+
p_count.write_volatile(0);
76+
77+
// Trigger segfaults
78+
STORAGE.add(0).write_volatile(1);
79+
STORAGE.add(PAGE_SIZE).write_volatile(1);
80+
STORAGE.add(0).write_volatile(1);
81+
STORAGE.add(PAGE_SIZE).write_volatile(1);
82+
STORAGE.add(0).write_volatile(1);
83+
STORAGE.add(PAGE_SIZE).write_volatile(1);
84+
85+
// The segfault handler should have been called for every
86+
// `write_volatile` in `STORAGE`. If the compiler ignores volatility,
87+
// some of these writes will be combined, causing a different number of
88+
// segfaults.
89+
//
90+
// This `p_count` read is done by a volatile read. If the compiler
91+
// ignores volatility, the compiler will speculate that `*p_count` is
92+
// unchanged and remove this check, failing the test.
93+
if p_count.read_volatile() != 6 {
94+
libc::puts(b"error: segfault count mismatch\0".as_ptr());
95+
return 1;
96+
}
97+
98+
0
99+
}
100+
}
101+
102+
unsafe extern "C" fn segv_handler(_: i32, _: *mut (), _: *mut ()) {
103+
let p_count = (&mut COUNT) as *mut u32;
104+
p_count.write_volatile(p_count.read_volatile() + 1);
105+
let count = p_count.read_volatile();
106+
107+
// Toggle the protected page so that the handler will be called for
108+
// each `write_volatile`
109+
libc::mprotect(
110+
STORAGE.cast(),
111+
PAGE_SIZE,
112+
if count % 2 == 1 { libc::PROT_READ | libc::PROT_WRITE } else { 0 },
113+
);
114+
libc::mprotect(
115+
STORAGE.add(PAGE_SIZE).cast(),
116+
PAGE_SIZE,
117+
if count % 2 == 0 { libc::PROT_READ | libc::PROT_WRITE } else { 0 },
118+
);
119+
}

0 commit comments

Comments
 (0)