detcore/syscalls/
memory.rs1use reverie::Error;
12use reverie::Guest;
13use reverie::syscalls;
14use reverie::syscalls::AddrMut;
15use reverie::syscalls::Errno;
16use reverie::syscalls::MemoryAccess;
17
18use crate::Detcore;
19use crate::RecordOrReplay;
20
21const PAGE_SIZE: usize = 4096;
22const MADV_GUARD_INSTALL: i32 = 102;
24const MADV_GUARD_REMOVE: i32 = 103;
25
26#[derive(Debug, Clone, Copy, Eq, PartialEq)]
27enum MadviseAction {
28 ForwardHint,
29 ForwardSemantic,
30 Ignore,
31 Reject(Errno),
32 Unknown,
33}
34
35const fn madvise_action(advice: i32) -> MadviseAction {
36 match advice {
37 libc::MADV_NORMAL | libc::MADV_RANDOM | libc::MADV_SEQUENTIAL | libc::MADV_WILLNEED => {
40 MadviseAction::ForwardHint
41 }
42
43 libc::MADV_DONTNEED
46 | libc::MADV_DONTFORK
47 | libc::MADV_DOFORK
48 | libc::MADV_DONTDUMP
49 | libc::MADV_DODUMP
50 | libc::MADV_WIPEONFORK
51 | libc::MADV_KEEPONFORK
52 | libc::MADV_DONTNEED_LOCKED
53 | MADV_GUARD_INSTALL
54 | MADV_GUARD_REMOVE => MadviseAction::ForwardSemantic,
55
56 libc::MADV_FREE
61 | libc::MADV_MERGEABLE
62 | libc::MADV_UNMERGEABLE
63 | libc::MADV_HUGEPAGE
64 | libc::MADV_NOHUGEPAGE
65 | libc::MADV_COLD
66 | libc::MADV_PAGEOUT => MadviseAction::Ignore,
67
68 libc::MADV_REMOVE => MadviseAction::Reject(Errno::EINVAL),
71
72 libc::MADV_POPULATE_READ | libc::MADV_POPULATE_WRITE | libc::MADV_COLLAPSE => {
76 MadviseAction::Reject(Errno::EINVAL)
77 }
78
79 libc::MADV_HWPOISON | libc::MADV_SOFT_OFFLINE => MadviseAction::Reject(Errno::EPERM),
82
83 _ => MadviseAction::Unknown,
84 }
85}
86
87fn validate_common_args(call: syscalls::Madvise) -> Result<(), Error> {
88 let start = call.addr().map(AddrMut::as_raw).unwrap_or(0);
89 if !start.is_multiple_of(PAGE_SIZE) {
90 return Err(Errno::EINVAL.into());
91 }
92 if call.len() == 0 {
93 return Ok(());
94 }
95
96 let end = start.checked_add(call.len()).ok_or(Errno::EINVAL)?;
97 end.checked_add(PAGE_SIZE - 1).ok_or(Errno::EINVAL)?;
98 Ok(())
99}
100
101impl<T: RecordOrReplay> Detcore<T> {
102 pub async fn handle_madvise<G: Guest<Self>>(
115 &self,
116 guest: &mut G,
117 call: syscalls::Madvise,
118 ) -> Result<i64, Error> {
119 let advice = call.advice();
120 let action = madvise_action(advice);
121 validate_common_args(call)?;
122
123 if call.len() == 0 {
124 return match action {
125 MadviseAction::Unknown => Err(Errno::EINVAL.into()),
126 _ => Ok(0),
127 };
128 }
129 if self.cfg.recordreplay_modes && action == MadviseAction::ForwardHint {
130 crate::detlog!(
131 "[dtid {}] madvise hint {} accepted as record/replay no-op",
132 guest.thread_state().dettid,
133 advice,
134 );
135 return Ok(0);
136 }
137
138 match action {
139 MadviseAction::ForwardHint if self.cfg.backend_supports_madvise => {
140 Ok(self.record_or_replay(guest, call).await?)
141 }
142 MadviseAction::ForwardHint => {
143 crate::detlog!(
144 "[dtid {}] madvise hint {} accepted as backend no-op",
145 guest.thread_state().dettid,
146 advice,
147 );
148 Ok(0)
149 }
150 MadviseAction::ForwardSemantic if self.cfg.backend_supports_madvise => {
151 Ok(self.record_or_replay(guest, call).await?)
152 }
153 MadviseAction::ForwardSemantic => {
154 crate::detlog!(
155 "[dtid {}] madvise advice {} is unsupported by this backend",
156 guest.thread_state().dettid,
157 advice,
158 );
159 Err(Errno::ENOSYS.into())
160 }
161 MadviseAction::Ignore => {
162 crate::detlog!(
163 "[dtid {}] madvise advice {} accepted as deterministic no-op",
164 guest.thread_state().dettid,
165 advice,
166 );
167 Ok(0)
168 }
169 MadviseAction::Reject(errno) => {
170 crate::detlog!(
171 "[dtid {}] madvise advice {} rejected with {}",
172 guest.thread_state().dettid,
173 advice,
174 errno,
175 );
176 Err(errno.into())
177 }
178 MadviseAction::Unknown => {
179 crate::detlog!(
180 "[dtid {}] unknown madvise advice {} rejected with EINVAL",
181 guest.thread_state().dettid,
182 advice,
183 );
184 Err(Errno::EINVAL.into())
185 }
186 }
187 }
188
189 pub async fn handle_mincore<G: Guest<Self>>(
204 &self,
205 guest: &mut G,
206 call: syscalls::Mincore,
207 ) -> Result<i64, Error> {
208 let start = call.addr().map(AddrMut::as_raw).unwrap_or(0);
210 if !start.is_multiple_of(PAGE_SIZE) {
211 return Err(Errno::EINVAL.into());
212 }
213 let len = call.len();
214 if len == 0 {
215 return Ok(0);
216 }
217 start.checked_add(len).ok_or(Errno::EINVAL)?;
219 let page_count = len.div_ceil(PAGE_SIZE);
220 let vec = call.vec().ok_or(Errno::EFAULT)?;
221 guest.inject(syscalls::Syscall::from(call)).await?;
222 let residency = vec![1u8; page_count];
223 guest.memory().write_exact(vec, &residency)?;
224 Ok(0)
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn madvise_known_linux_advice_has_an_explicit_policy() {
234 for advice in [
235 libc::MADV_NORMAL,
236 libc::MADV_RANDOM,
237 libc::MADV_SEQUENTIAL,
238 libc::MADV_WILLNEED,
239 ] {
240 assert_eq!(madvise_action(advice), MadviseAction::ForwardHint);
241 }
242
243 for advice in [
244 libc::MADV_DONTNEED,
245 libc::MADV_DONTFORK,
246 libc::MADV_DOFORK,
247 libc::MADV_DONTDUMP,
248 libc::MADV_DODUMP,
249 libc::MADV_WIPEONFORK,
250 libc::MADV_KEEPONFORK,
251 libc::MADV_DONTNEED_LOCKED,
252 MADV_GUARD_INSTALL,
253 MADV_GUARD_REMOVE,
254 ] {
255 assert_eq!(madvise_action(advice), MadviseAction::ForwardSemantic);
256 }
257
258 for advice in [
259 libc::MADV_FREE,
260 libc::MADV_MERGEABLE,
261 libc::MADV_UNMERGEABLE,
262 libc::MADV_HUGEPAGE,
263 libc::MADV_NOHUGEPAGE,
264 libc::MADV_COLD,
265 libc::MADV_PAGEOUT,
266 ] {
267 assert_eq!(madvise_action(advice), MadviseAction::Ignore);
268 }
269
270 for advice in [
271 libc::MADV_REMOVE,
272 libc::MADV_POPULATE_READ,
273 libc::MADV_POPULATE_WRITE,
274 libc::MADV_COLLAPSE,
275 ] {
276 assert_eq!(madvise_action(advice), MadviseAction::Reject(Errno::EINVAL));
277 }
278 for advice in [libc::MADV_HWPOISON, libc::MADV_SOFT_OFFLINE] {
279 assert_eq!(madvise_action(advice), MadviseAction::Reject(Errno::EPERM));
280 }
281 assert_eq!(madvise_action(i32::MAX), MadviseAction::Unknown);
282 }
283
284 #[test]
285 fn common_argument_validation_is_host_independent() {
286 let aligned = unsafe { AddrMut::<libc::c_void>::from_raw_unchecked(0x1000) };
287 assert!(
288 validate_common_args(
289 syscalls::Madvise::new()
290 .with_addr(Some(aligned))
291 .with_len(0)
292 .with_advice(libc::MADV_FREE),
293 )
294 .is_ok()
295 );
296
297 let unaligned = unsafe { AddrMut::<libc::c_void>::from_raw_unchecked(0x1001) };
298 assert!(
299 validate_common_args(
300 syscalls::Madvise::new()
301 .with_addr(Some(unaligned))
302 .with_len(0)
303 .with_advice(libc::MADV_FREE),
304 )
305 .is_err()
306 );
307
308 let near_end =
309 unsafe { AddrMut::<libc::c_void>::from_raw_unchecked(usize::MAX & !(PAGE_SIZE - 1)) };
310 assert!(
311 validate_common_args(
312 syscalls::Madvise::new()
313 .with_addr(Some(near_end))
314 .with_len(PAGE_SIZE)
315 .with_advice(libc::MADV_FREE),
316 )
317 .is_err()
318 );
319 }
320}