1use core::ffi::CStr;
2use core::fmt::Formatter;
3
4#[cfg(target_os = "linux")]
5use libc::*;
6
7#[derive(Debug)]
8pub enum Error {
9 IoError(&'static str, c_int),
10 InvalidFormat(&'static str),
11 ParseIntError(core::num::ParseIntError),
12}
13
14impl From<core::num::ParseIntError> for Error {
15 fn from(e: core::num::ParseIntError) -> Self {
16 Error::ParseIntError(e)
17 }
18}
19
20impl std::error::Error for Error {
23 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
24 match self {
25 Error::IoError(_, _) => None,
26 Error::InvalidFormat(_) => None,
27 Error::ParseIntError(e) => Some(e),
28 }
29 }
30}
31
32impl core::fmt::Display for Error {
33 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
34 match self {
35 Error::IoError(s, e) => write!(f, "IoError: {} {}", s, e),
36 Error::InvalidFormat(s) => write!(f, "InvalidFormat: {}", s),
37 Error::ParseIntError(e) => write!(f, "ParseIntError: {}", e),
38 }
39 }
40}
41
42#[cfg(target_os = "linux")]
44unsafe fn unmap_region(address: *mut c_void, size: size_t) -> Result<(), Error> {
45 let errno = munmap(address, size);
46 if errno == 0 {
47 Ok(())
48 } else {
49 Err(Error::IoError("munmap", errno))
50 }
51}
52
53#[cfg(feature = "test-clock")]
58#[cfg(target_os = "linux")]
59pub fn test_clock() -> ! {
60 let mut ts = timespec {
61 tv_sec: 0,
62 tv_nsec: 0,
63 };
64
65 let result = unsafe { clock_gettime(CLOCK_MONOTONIC, &mut ts) };
66
67 if result == 0 {
68 panic!("clock_gettime succeeded when it should have failed");
69 } else {
70 panic!("clock_gettime failed as expected, but code continued for some reason!");
71 }
72}
73
74#[cfg(feature = "test-clock")]
75#[cfg(not(target_os = "linux"))]
76pub fn test_clock() -> ! {
77 panic!("test_clock is only available on linux");
78}
79
80#[cfg(target_os = "linux")]
86fn find_mapping_addresses() -> Result<
87 (
88 Option<(*mut libc::c_void, libc::size_t)>,
89 Option<(*mut libc::c_void, libc::size_t)>,
90 ),
91 Error,
92> {
93 let path = unsafe {
94 CStr::from_bytes_until_nul(b"/proc/self/maps\x00").unwrap_unchecked()
96 };
97 let fd = unsafe { open(path.as_ptr(), O_RDONLY) };
98 if fd < 0 {
99 return Err(Error::IoError("open", fd));
100 }
101
102 let mut buffer = [0u8; 4096];
105 let mut line = [0u8; 1024];
108 let mut line_idx = 0;
109 let mut vvar = None;
110 let mut vdso = None;
111
112 loop {
113 let bytes_read =
114 unsafe { read(fd, buffer.as_mut_ptr() as *mut libc::c_void, buffer.len()) };
115 if bytes_read <= 0 {
116 break; }
118
119 for &byte in &buffer[..bytes_read as usize] {
120 if byte == b'\n' {
121 if line.windows(6).any(|window| window == b"[vdso]") {
122 vdso = Some(parse_addresses(&line[..12], &line[13..25])?);
123 } else if line.windows(6).any(|window| window == b"[vvar]") {
124 vvar = Some(parse_addresses(&line[..12], &line[13..25])?);
125 }
126 line_idx = 0; } else {
128 if line_idx < line.len() {
129 line[line_idx] = byte;
130 line_idx += 1;
131 }
132 }
133 }
134 }
135
136 unsafe { close(fd) };
137 Ok((vvar, vdso))
138}
139
140fn parse_addresses(
141 start_addr: &[u8],
142 end_addr: &[u8],
143) -> Result<(*mut libc::c_void, libc::size_t), Error> {
144 let start = parse_hex_address(start_addr)?;
145 let end = parse_hex_address(end_addr)?;
146
147 Ok((start as *mut libc::c_void, end - start))
148}
149
150fn parse_hex_address(addr: &[u8]) -> Result<usize, Error> {
151 let mut num = 0;
152 for &byte in addr {
153 num = num * 16
154 + match byte {
155 b'0'..=b'9' => byte - b'0',
156 b'a'..=b'f' => 10 + byte - b'a',
157 b'A'..=b'F' => 10 + byte - b'A',
158 _ => return Err(Error::InvalidFormat("Invalid hexadecimal number")),
159 } as usize;
160 }
161 Ok(num)
162}
163
164#[cfg(target_os = "linux")]
165fn allocate_guard_page(address: *mut c_void, size: size_t) -> Result<(), Error> {
166 let result = unsafe {
167 mmap(
168 address,
169 size,
170 PROT_NONE,
171 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
172 -1,
173 0,
174 )
175 };
176
177 if result == libc::MAP_FAILED {
178 Err(Error::IoError("mmap", result as c_int))
179 } else {
180 Ok(())
181 }
182}
183
184#[cfg(target_os = "linux")]
186pub fn remove_timer_mappings() -> Result<(), Error> {
187 let (Some((vdso_address, vdso_size)), Some((vvar_address, vvar_size))) =
188 find_mapping_addresses()?
189 else {
190 return Err(Error::InvalidFormat("Could not find vdso or vvar mappings"));
191 };
192 unsafe {
194 unmap_region(vdso_address, vdso_size)?;
195 unmap_region(vvar_address, vvar_size)?;
196 }
197 Ok(())
198}
199
200#[cfg(target_os = "linux")]
202pub fn replace_timer_mappings() -> Result<(), Error> {
203 let (Some((vdso_address, vdso_size)), Some((vvar_address, vvar_size))) =
204 find_mapping_addresses()?
205 else {
206 return Err(Error::InvalidFormat("Could not find vdso or vvar mappings"));
207 };
208 unsafe {
210 unmap_region(vdso_address, vdso_size)?;
211 unmap_region(vvar_address, vvar_size)?;
212 }
213
214 allocate_guard_page(vdso_address, vdso_size)?;
215 allocate_guard_page(vvar_address, vvar_size)?;
216
217 Ok(())
218}
219
220#[cfg(not(target_os = "linux"))]
221pub fn remove_timer_mappings() -> Result<(), Error> {
222 Ok(())
223}
224
225#[cfg(not(target_os = "linux"))]
226pub fn replace_timer_mappings() -> Result<(), Error> {
227 Ok(())
228}