shadow_rs/host/syscall/handler/
time.rs

1use linux_api::errno::Errno;
2use linux_api::time::{ClockId, ClockNanosleepFlags, ITimerId};
3use log::*;
4use shadow_shim_helper_rs::emulated_time::EmulatedTime;
5use shadow_shim_helper_rs::simulation_time::SimulationTime;
6use shadow_shim_helper_rs::syscall_types::ForeignPtr;
7
8use crate::core::worker::Worker;
9use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
10use crate::host::syscall::types::SyscallError;
11use crate::host::timer::Timer;
12
13fn kernel_old_itimerval_from_timer(timer: &Timer) -> linux_api::time::kernel_old_itimerval {
14    linux_api::time::kernel_old_itimerval {
15        it_interval: timer
16            .expire_interval()
17            .unwrap_or(SimulationTime::ZERO)
18            .try_into()
19            .unwrap(),
20        it_value: timer
21            .remaining_time()
22            .unwrap_or(SimulationTime::ZERO)
23            .try_into()
24            .unwrap(),
25    }
26}
27
28impl SyscallHandler {
29    log_syscall!(
30        getitimer,
31        /* rv */ std::ffi::c_int,
32        /* which */ linux_api::time::ITimerId,
33        /*curr_value*/ *const linux_api::time::kernel_old_itimerval,
34    );
35    pub fn getitimer(
36        ctx: &mut SyscallContext,
37        which: std::ffi::c_int,
38        curr_value_ptr: ForeignPtr<linux_api::time::kernel_old_itimerval>,
39    ) -> Result<(), SyscallError> {
40        let Ok(which) = ITimerId::try_from(which) else {
41            debug!("Bad itimerid {which}");
42            return Err(Errno::EINVAL.into());
43        };
44
45        if which != ITimerId::ITIMER_REAL {
46            warn_once_then_debug!("Timer type {which:?} unsupported");
47            return Err(Errno::EINVAL.into());
48        }
49
50        let itimerval = kernel_old_itimerval_from_timer(&ctx.objs.process.realtime_timer_borrow());
51        ctx.objs
52            .process
53            .memory_borrow_mut()
54            .write(curr_value_ptr, &itimerval)?;
55
56        Ok(())
57    }
58
59    log_syscall!(
60        setitimer,
61        /* rv */ std::ffi::c_int,
62        /* which */ linux_api::time::ITimerId,
63        /* new_value */ *const linux_api::time::kernel_old_itimerval,
64        /* old_value */ *const linux_api::time::kernel_old_itimerval,
65    );
66    pub fn setitimer(
67        ctx: &mut SyscallContext,
68        which: std::ffi::c_int,
69        new_value_ptr: ForeignPtr<linux_api::time::kernel_old_itimerval>,
70        old_value_ptr: ForeignPtr<linux_api::time::kernel_old_itimerval>,
71    ) -> Result<(), SyscallError> {
72        let Ok(which) = ITimerId::try_from(which) else {
73            debug!("Bad itimerid {which}");
74            return Err(Errno::EINVAL.into());
75        };
76
77        if which != ITimerId::ITIMER_REAL {
78            warn_once_then_debug!("Timer type {which:?} unsupported");
79            return Err(Errno::EINVAL.into());
80        }
81
82        if !old_value_ptr.is_null() {
83            let itimerval =
84                kernel_old_itimerval_from_timer(&ctx.objs.process.realtime_timer_borrow());
85            ctx.objs
86                .process
87                .memory_borrow_mut()
88                .write(old_value_ptr, &itimerval)?;
89        }
90
91        let new_value = ctx.objs.process.memory_borrow().read(new_value_ptr)?;
92        let new_value_value =
93            SimulationTime::try_from(new_value.it_value).map_err(|_| Errno::EINVAL)?;
94        let new_value_interval =
95            SimulationTime::try_from(new_value.it_interval).map_err(|_| Errno::EINVAL)?;
96
97        if new_value_value == SimulationTime::ZERO {
98            ctx.objs.process.realtime_timer_borrow_mut().disarm();
99        } else {
100            ctx.objs.process.realtime_timer_borrow_mut().arm(
101                ctx.objs.host,
102                Worker::current_time().unwrap() + new_value_value,
103                new_value_interval
104                    .is_positive()
105                    .then_some(new_value_interval),
106            );
107        }
108
109        Ok(())
110    }
111
112    log_syscall!(
113        alarm,
114        /* rv */ std::ffi::c_uint,
115        /* seconds */ std::ffi::c_uint,
116    );
117    pub fn alarm(
118        ctx: &mut SyscallContext,
119        seconds: std::ffi::c_uint,
120    ) -> Result<std::ffi::c_uint, SyscallError> {
121        let prev_remaining = ctx.objs.process.realtime_timer_borrow().remaining_time();
122        let prev_remaining_secs = match prev_remaining {
123            Some(t) => {
124                let t = std::time::Duration::from(t);
125                if t.as_secs() == 0 {
126                    // Round up [0..1) to 1, so that we never return 0 if there
127                    // was a timer set. Even if t is exactly 0 (the timer was
128                    // schedule to fire at exactly now, but hasn't yet), we want
129                    // to return 1.
130                    1
131                } else if t.subsec_millis() > 500 {
132                    // Round up to the nearest second
133                    t.as_secs() + 1
134                } else {
135                    // Round down to the nearest second
136                    t.as_secs()
137                }
138            }
139            None => 0,
140        };
141        // The returned value is defined to be u32.
142        let prev_remaining_secs: u32 = u32::try_from(prev_remaining_secs).unwrap_or_else(|_| {
143            // unclear what we ought to do if it doesn't fit, or whether
144            // it's even possible to set a timer that far in the future in
145            // the first place.
146            debug!("Couldn't convert remaining time {prev_remaining:?} to u32; using u32::MAX");
147            u32::MAX
148        });
149
150        if seconds == 0 {
151            // alarm(2): If seconds is zero, any pending alarm is canceled.
152            ctx.objs.process.realtime_timer_borrow_mut().disarm();
153        } else {
154            // Otherwise arm the timer for the specified number of seconds
155            // (implicitly canceling the previous timer if there was one).
156            ctx.objs.process.realtime_timer_borrow_mut().arm(
157                ctx.objs.host,
158                Worker::current_time().unwrap() + SimulationTime::from_secs(seconds.into()),
159                None,
160            );
161        }
162
163        Ok(prev_remaining_secs)
164    }
165
166    log_syscall!(
167        clock_getres,
168        /* rv */ std::ffi::c_int,
169        /* clock_id */ linux_api::time::ClockId,
170        /* res */ *const std::ffi::c_void,
171    );
172    pub fn clock_getres(
173        ctx: &mut SyscallContext,
174        clock_id: linux_api::time::linux___kernel_clockid_t,
175        res_ptr: ForeignPtr<linux_api::time::timespec>,
176    ) -> Result<(), SyscallError> {
177        // Make sure we have a valid clock id.
178        ClockId::try_from(clock_id).map_err(|_| Errno::EINVAL)?;
179
180        // All clocks have nanosecond resolution.
181        if !res_ptr.is_null() {
182            let res_time = linux_api::time::timespec::try_from(SimulationTime::NANOSECOND).unwrap();
183            ctx.objs
184                .process
185                .memory_borrow_mut()
186                .write(res_ptr, &res_time)?;
187        }
188
189        Ok(())
190    }
191
192    log_syscall!(
193        clock_nanosleep,
194        /* rv */ std::ffi::c_int,
195        /* clock_id */ linux_api::time::ClockId,
196        /* flags */ linux_api::time::ClockNanosleepFlags,
197        /* request */ *const linux_api::time::timespec,
198        /* remain */ *const std::ffi::c_void,
199    );
200    pub fn clock_nanosleep(
201        ctx: &mut SyscallContext,
202        clock_id: linux_api::time::linux___kernel_clockid_t,
203        flags: std::ffi::c_int,
204        request_ptr: ForeignPtr<linux_api::time::timespec>,
205        remain_ptr: ForeignPtr<linux_api::time::timespec>,
206    ) -> Result<(), SyscallError> {
207        let clock_id = ClockId::try_from(clock_id).map_err(|_| Errno::EINVAL)?;
208
209        // Check for clock_ids that specifically support nanosleep.
210        if [
211            ClockId::CLOCK_MONOTONIC,
212            ClockId::CLOCK_REALTIME,
213            ClockId::CLOCK_BOOTTIME,
214            ClockId::CLOCK_TAI,
215            ClockId::CLOCK_REALTIME_ALARM,
216            ClockId::CLOCK_BOOTTIME_ALARM,
217        ]
218        .contains(&clock_id)
219        {
220            // Simulated in Shadow; Linux allows unspec bitflags, but not for the *ALARM clocks.
221            let allow_unspec_bitflags =
222                ![ClockId::CLOCK_REALTIME_ALARM, ClockId::CLOCK_BOOTTIME_ALARM].contains(&clock_id);
223            Self::nanosleep_helper(ctx, flags, request_ptr, remain_ptr, allow_unspec_bitflags)
224        } else if [ClockId::CLOCK_THREAD_CPUTIME_ID].contains(&clock_id) {
225            // Invalid in Linux.
226            log::debug!("Invalid clock id {clock_id:?}.",);
227            Err(Errno::EINVAL.into())
228        } else if [
229            ClockId::CLOCK_MONOTONIC_RAW,
230            ClockId::CLOCK_REALTIME_COARSE,
231            ClockId::CLOCK_MONOTONIC_COARSE,
232        ]
233        .contains(&clock_id)
234        {
235            // Not supported in Linux.
236            log::debug!("Clock id {clock_id:?} unsupported for clock_nanosleep.",);
237            Err(Errno::ENOTSUP.into())
238        } else if [ClockId::CLOCK_PROCESS_CPUTIME_ID].contains(&clock_id) {
239            // Supported in Linux, not in Shadow.
240            warn_once_then_debug!("Clock id {clock_id:?} unsupported in Shadow.",);
241            Err(Errno::ENOTSUP.into())
242        } else {
243            log::debug!("Unknown clock id {clock_id:?}.");
244            Err(Errno::EINVAL.into())
245        }
246    }
247
248    log_syscall!(
249        nanosleep,
250        /* rv */ std::ffi::c_int,
251        /* req */ *const linux_api::time::timespec,
252        /* rem */ *const std::ffi::c_void,
253    );
254    pub fn nanosleep(
255        ctx: &mut SyscallContext,
256        req: ForeignPtr<linux_api::time::timespec>,
257        rem: ForeignPtr<linux_api::time::timespec>,
258    ) -> Result<(), SyscallError> {
259        Self::nanosleep_helper(ctx, 0, req, rem, false)
260    }
261
262    fn nanosleep_helper(
263        ctx: &mut SyscallContext,
264        flags: std::ffi::c_int,
265        request_ptr: ForeignPtr<linux_api::time::timespec>,
266        remain_ptr: ForeignPtr<linux_api::time::timespec>,
267        allow_unspec_bitflags: bool,
268    ) -> Result<(), SyscallError> {
269        let request = ctx.objs.process.memory_borrow().read(request_ptr)?;
270        let request_time = SimulationTime::try_from(request).or(Err(Errno::EINVAL))?;
271        let flags = if allow_unspec_bitflags {
272            ClockNanosleepFlags::from_bits_truncate(flags)
273        } else {
274            ClockNanosleepFlags::from_bits(flags).ok_or(Errno::EINVAL)?
275        };
276
277        let now = Worker::current_time().unwrap();
278
279        // The requested wakeup time may be absolute or relative.
280        let abs_wakeup_time = if flags.contains(ClockNanosleepFlags::TIMER_ABSTIME) {
281            EmulatedTime::UNIX_EPOCH + request_time
282        } else {
283            now + request_time
284        };
285
286        // A wakeup time in the past means we return without sleeping.
287        if abs_wakeup_time <= now {
288            return Ok(());
289        }
290
291        // Condition will exist after a wakeup.
292        let Some(cond) = ctx.objs.thread.syscall_condition() else {
293            // Didn't sleep yet; block the thread now.
294            return Err(SyscallError::new_blocked_until(abs_wakeup_time, false));
295        };
296
297        // Woke up from sleep. We must have set a timeout to sleep.
298        let expected_wakeup_time = cond.timeout().unwrap();
299
300        if expected_wakeup_time <= now {
301            // Successful sleep and wakeup!
302            Ok(())
303        } else {
304            // Possibly write out the remaining time until the expected wakeup.
305            if !remain_ptr.is_null() && !flags.contains(ClockNanosleepFlags::TIMER_ABSTIME) {
306                let remain_time =
307                    linux_api::time::timespec::try_from(expected_wakeup_time - now).unwrap();
308                ctx.objs
309                    .process
310                    .memory_borrow_mut()
311                    .write(remain_ptr, &remain_time)?;
312            }
313
314            // Encodes that we were interrupted but will return EINTR to the plugin.
315            Err(SyscallError::new_interrupted(false))
316        }
317    }
318}