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