1use std::marker::PhantomData;
4use std::mem::size_of;
5
6use linux_api::errno::Errno;
7use log::Level::Debug;
8use log::*;
9use shadow_shim_helper_rs::emulated_time::EmulatedTime;
10use shadow_shim_helper_rs::syscall_types::{ForeignPtr, SyscallReg};
11
12use crate::cshadow as c;
13use crate::host::descriptor::{File, FileState};
14use crate::host::futex_table::FutexRef;
15use crate::host::syscall::Trigger;
16use crate::host::syscall::condition::SyscallCondition;
17
18#[derive(Copy, Clone)]
20pub struct ForeignArrayPtr<T> {
21 base: ForeignPtr<T>,
22 count: usize,
23 _phantom: std::marker::PhantomData<T>,
24}
25
26impl<T> std::fmt::Debug for ForeignArrayPtr<T> {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.debug_struct("ForeignArrayPtr")
29 .field("base", &self.base)
30 .field("count", &self.count)
31 .field("size_of::<T>", &size_of::<T>())
32 .finish()
33 }
34}
35
36impl<T> ForeignArrayPtr<T> {
37 pub fn new(ptr: ForeignPtr<T>, count: usize) -> Self {
40 if log_enabled!(Debug) && usize::from(ptr) % std::mem::align_of::<T>() != 0 {
41 debug!(
52 "Creating unaligned pointer {ptr:?}. This is legal, but could trigger latent bugs."
53 );
54 }
55 ForeignArrayPtr {
56 base: ptr,
57 count,
58 _phantom: PhantomData,
59 }
60 }
61
62 pub fn ptr(&self) -> ForeignPtr<T> {
64 self.base
65 }
66
67 pub fn len(&self) -> usize {
69 self.count
70 }
71
72 pub fn is_empty(&self) -> bool {
73 self.count == 0
74 }
75
76 pub fn is_null(&self) -> bool {
77 self.base.is_null()
78 }
79
80 pub fn cast<U>(&self) -> Option<ForeignArrayPtr<U>> {
82 let count_bytes = self.count * size_of::<T>();
83 if count_bytes % size_of::<U>() != 0 {
84 return None;
85 }
86 Some(ForeignArrayPtr::new(
87 self.base.cast::<U>(),
88 count_bytes / size_of::<U>(),
89 ))
90 }
91
92 pub fn cast_u8(&self) -> ForeignArrayPtr<u8> {
94 self.cast::<u8>().unwrap()
95 }
96
97 pub fn slice<R: std::ops::RangeBounds<usize>>(&self, range: R) -> ForeignArrayPtr<T> {
99 use std::ops::Bound;
100 let excluded_end = match range.end_bound() {
101 Bound::Included(e) => e + 1,
102 Bound::Excluded(e) => *e,
103 Bound::Unbounded => self.count,
104 };
105 let included_start = match range.start_bound() {
106 Bound::Included(s) => *s,
107 Bound::Excluded(s) => s + 1,
108 Bound::Unbounded => 0,
109 };
110 assert!(included_start <= excluded_end);
111 assert!(excluded_end <= self.count);
112 assert!(included_start <= self.count);
115
116 ForeignArrayPtr {
117 base: self.base.add(included_start),
118 count: excluded_end - included_start,
119 _phantom: PhantomData,
120 }
121 }
122}
123
124#[derive(Debug, PartialEq, Eq)]
127pub enum SyscallError {
128 Failed(Failed),
129 Blocked(Blocked),
130 Native,
131}
132
133#[derive(Debug, PartialEq, Eq)]
134pub struct Blocked {
135 pub condition: SyscallCondition,
136 pub restartable: bool,
137}
138
139#[derive(Debug, PartialEq, Eq)]
140pub struct Failed {
141 pub errno: linux_api::errno::Errno,
142 pub restartable: bool,
143}
144
145pub type SyscallResult = Result<SyscallReg, SyscallError>;
146
147impl From<SyscallReturn> for SyscallResult {
148 fn from(r: SyscallReturn) -> Self {
149 match r {
150 SyscallReturn::Done(done) => {
151 match crate::utility::syscall::raw_return_value_to_result(i64::from(done.retval)) {
152 Ok(r) => Ok(r),
153 Err(e) => Err(SyscallError::Failed(Failed {
154 errno: e,
155 restartable: done.restartable,
156 })),
157 }
158 }
159 SyscallReturn::Block(blocked) => Err(SyscallError::Blocked(Blocked {
161 condition: unsafe { SyscallCondition::consume_from_c(blocked.cond) },
162 restartable: blocked.restartable,
163 })),
164 SyscallReturn::Native => Err(SyscallError::Native),
165 }
166 }
167}
168
169impl From<SyscallResult> for SyscallReturn {
170 fn from(syscall_return: SyscallResult) -> Self {
171 match syscall_return {
172 Ok(r) => SyscallReturn::Done(SyscallReturnDone {
173 retval: r,
174 restartable: false,
176 }),
177 Err(SyscallError::Failed(failed)) => SyscallReturn::Done(SyscallReturnDone {
178 retval: (-(i64::from(failed.errno))).into(),
179 restartable: failed.restartable,
180 }),
181 Err(SyscallError::Blocked(blocked)) => SyscallReturn::Block(SyscallReturnBlocked {
182 cond: blocked.condition.into_inner(),
183 restartable: blocked.restartable,
184 }),
185 Err(SyscallError::Native) => SyscallReturn::Native,
186 }
187 }
188}
189
190impl From<linux_api::errno::Errno> for SyscallError {
191 fn from(e: linux_api::errno::Errno) -> Self {
192 SyscallError::Failed(Failed {
193 errno: e,
194 restartable: false,
195 })
196 }
197}
198
199impl From<std::io::Error> for SyscallError {
200 fn from(e: std::io::Error) -> Self {
201 match std::io::Error::raw_os_error(&e) {
202 Some(e) => SyscallError::Failed(Failed {
203 errno: Errno::try_from(u16::try_from(e).unwrap()).unwrap(),
206 restartable: false,
207 }),
208 None => {
209 let default = Errno::ENOTSUP;
210 warn!("Mapping error {e} to {default}");
211 SyscallError::from(default)
212 }
213 }
214 }
215}
216
217impl SyscallError {
218 pub fn new_blocked_on_file(file: File, state: FileState, restartable: bool) -> Self {
219 Self::Blocked(Blocked {
220 condition: SyscallCondition::new(Trigger::from_file(file, state)),
221 restartable,
222 })
223 }
224
225 pub fn new_blocked_on_child(restartable: bool) -> Self {
226 Self::Blocked(Blocked {
227 condition: SyscallCondition::new(Trigger::child()),
228 restartable,
229 })
230 }
231
232 pub fn new_blocked_on_futex(futex: FutexRef, restartable: bool) -> Self {
233 Self::Blocked(Blocked {
234 condition: SyscallCondition::new(Trigger::from_futex(futex)),
235 restartable,
236 })
237 }
238
239 pub fn new_blocked_until(unblock_time: EmulatedTime, restartable: bool) -> Self {
240 Self::Blocked(Blocked {
241 condition: SyscallCondition::new_from_wakeup_time(unblock_time),
242 restartable,
243 })
244 }
245
246 pub fn new_interrupted(restartable: bool) -> Self {
247 Self::Failed(Failed {
248 errno: Errno::EINTR,
249 restartable,
250 })
251 }
252
253 pub fn blocked_condition(&mut self) -> Option<&mut SyscallCondition> {
255 if let Self::Blocked(Blocked { condition, .. }) = self {
256 Some(condition)
257 } else {
258 None
259 }
260 }
261}
262
263#[derive(Copy, Clone, Debug)]
264#[repr(C)]
265pub struct SyscallReturnDone {
266 pub retval: SyscallReg,
267 pub restartable: bool,
271}
272
273#[derive(Copy, Clone, Debug)]
274#[repr(C)]
275pub struct SyscallReturnBlocked {
276 pub cond: *mut c::SysCallCondition,
277 pub restartable: bool,
281}
282
283#[derive(Copy, Clone, Debug)]
284#[repr(i8, C)]
285pub enum SyscallReturn {
286 Done(SyscallReturnDone),
288 Block(SyscallReturnBlocked),
290 Native,
292}
293
294mod export {
295 use shadow_shim_helper_rs::syscall_types::UntypedForeignPtr;
296
297 use super::*;
298
299 #[unsafe(no_mangle)]
300 pub unsafe extern "C-unwind" fn syscallreturn_makeDone(retval: SyscallReg) -> SyscallReturn {
301 SyscallReturn::Done(SyscallReturnDone {
302 retval,
303 restartable: false,
304 })
305 }
306
307 #[unsafe(no_mangle)]
308 pub unsafe extern "C-unwind" fn syscallreturn_makeDoneI64(retval: i64) -> SyscallReturn {
309 SyscallReturn::Done(SyscallReturnDone {
310 retval: retval.into(),
311 restartable: false,
312 })
313 }
314
315 #[unsafe(no_mangle)]
316 pub unsafe extern "C-unwind" fn syscallreturn_makeDoneU64(retval: u64) -> SyscallReturn {
317 SyscallReturn::Done(SyscallReturnDone {
318 retval: retval.into(),
319 restartable: false,
320 })
321 }
322
323 #[unsafe(no_mangle)]
324 pub unsafe extern "C-unwind" fn syscallreturn_makeDonePtr(
325 retval: UntypedForeignPtr,
326 ) -> SyscallReturn {
327 SyscallReturn::Done(SyscallReturnDone {
328 retval: retval.into(),
329 restartable: false,
330 })
331 }
332
333 #[unsafe(no_mangle)]
334 pub unsafe extern "C-unwind" fn syscallreturn_makeDoneErrno(err: i32) -> SyscallReturn {
335 debug_assert!(err > 0);
336 debug_assert!(err != libc::EINTR);
338 SyscallReturn::Done(SyscallReturnDone {
339 retval: (-err).into(),
340 restartable: false,
341 })
342 }
343
344 #[unsafe(no_mangle)]
345 pub unsafe extern "C-unwind" fn syscallreturn_makeInterrupted(
346 restartable: bool,
347 ) -> SyscallReturn {
348 SyscallReturn::Done(SyscallReturnDone {
349 retval: (-libc::EINTR).into(),
350 restartable,
351 })
352 }
353
354 #[unsafe(no_mangle)]
355 pub unsafe extern "C-unwind" fn syscallreturn_makeBlocked(
356 cond: *mut c::SysCallCondition,
357 restartable: bool,
358 ) -> SyscallReturn {
359 SyscallReturn::Block(SyscallReturnBlocked { cond, restartable })
360 }
361
362 #[unsafe(no_mangle)]
363 pub unsafe extern "C-unwind" fn syscallreturn_makeNative() -> SyscallReturn {
364 SyscallReturn::Native
365 }
366
367 #[unsafe(no_mangle)]
368 pub unsafe extern "C-unwind" fn syscallreturn_blocked(
369 scr: *mut SyscallReturn,
370 ) -> *mut SyscallReturnBlocked {
371 let scr = unsafe { scr.as_mut().unwrap() };
372 let SyscallReturn::Block(b) = scr else {
373 panic!("Unexpected scr {scr:?}");
374 };
375 b
376 }
377
378 #[unsafe(no_mangle)]
379 pub unsafe extern "C-unwind" fn syscallreturn_done(
380 scr: *mut SyscallReturn,
381 ) -> *mut SyscallReturnDone {
382 let scr = unsafe { scr.as_mut().unwrap() };
383 let SyscallReturn::Done(d) = scr else {
384 panic!("Unexpected scr {scr:?}");
385 };
386 d
387 }
388}