detcore/syscalls/
socket_timestamp_ioctl.rs1use reverie::Error;
12use reverie::Guest;
13use reverie::syscalls;
14use reverie::syscalls::AddrMut;
15use reverie::syscalls::Errno;
16use reverie::syscalls::MemoryAccess;
17use reverie::syscalls::ioctl::Request;
18
19use crate::fd::FdType;
20use crate::record_or_replay::RecordOrReplay;
21use crate::tool_local::Detcore;
22use crate::types::LogicalTime;
23
24const SIOCGSTAMPNS_OLD: usize = 0x8907;
25const SIOCGSTAMP_NEW: usize = 0x8010_8906;
26const SIOCGSTAMPNS_NEW: usize = 0x8010_8907;
27
28#[derive(Clone, Copy)]
29enum TimestampOutput {
30 Timeval(usize),
31 Timespec(usize),
32}
33
34fn timestamp_output(call: syscalls::Ioctl) -> Option<TimestampOutput> {
35 match call.request() {
36 Request::SIOCGSTAMP(address) => Some(TimestampOutput::Timeval(
37 address.map_or(0, |address| address.as_raw()),
38 )),
39 Request::Other(SIOCGSTAMP_NEW, address) => Some(TimestampOutput::Timeval(address)),
40 Request::Other(SIOCGSTAMPNS_OLD | SIOCGSTAMPNS_NEW, address) => {
41 Some(TimestampOutput::Timespec(address))
42 }
43 _ => None,
44 }
45}
46
47pub(crate) fn is_socket_timestamp_ioctl(call: syscalls::Ioctl) -> bool {
49 matches!(
50 call.request(),
51 Request::SIOCGSTAMP(_)
52 | Request::Other(SIOCGSTAMP_NEW | SIOCGSTAMPNS_OLD | SIOCGSTAMPNS_NEW, _,)
53 )
54}
55
56fn logical_timeval(now: LogicalTime) -> libc::timeval {
57 libc::timeval {
58 tv_sec: now.as_secs() as libc::time_t,
59 tv_usec: (now.subsec_nanos() / 1_000) as libc::suseconds_t,
60 }
61}
62
63fn logical_timespec(now: LogicalTime) -> libc::timespec {
64 libc::timespec {
65 tv_sec: now.as_secs() as libc::time_t,
66 tv_nsec: now.subsec_nanos() as libc::c_long,
67 }
68}
69
70impl<T: RecordOrReplay> Detcore<T> {
71 pub async fn handle_socket_timestamp_ioctl<G: Guest<Self>>(
75 &self,
76 guest: &mut G,
77 call: syscalls::Ioctl,
78 ) -> Result<i64, Error> {
79 let output = timestamp_output(call).ok_or(Errno::EFAULT)?;
80 let recorded_call = match output {
86 TimestampOutput::Timeval(address) => {
87 call.with_request(Request::Other(SIOCGSTAMP_NEW, address))
88 }
89 TimestampOutput::Timespec(address) => {
90 call.with_request(Request::Other(SIOCGSTAMPNS_NEW, address))
91 }
92 };
93 if !self.cfg.virtualize_time {
94 return Ok(self.record_or_replay(guest, recorded_call).await?);
95 }
96
97 let now = guest.thread_state().with_detfd(call.fd(), |detfd| {
98 if detfd.ty() != FdType::Socket {
99 return Err(Errno::ENOTTY);
100 }
101 detfd.socket_receive_timestamp().ok_or(Errno::ENOENT)
102 })??;
103 match output {
104 TimestampOutput::Timeval(address) => {
105 let address = AddrMut::from_raw(address).ok_or(Errno::EFAULT)?;
106 guest.memory().write_value(address, &logical_timeval(now))?;
107 }
108 TimestampOutput::Timespec(address) => {
109 let address = AddrMut::from_raw(address).ok_or(Errno::EFAULT)?;
110 guest
111 .memory()
112 .write_value(address, &logical_timespec(now))?;
113 }
114 }
115 Ok(0)
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn logical_socket_timestamps_use_linux_units() {
125 let now = LogicalTime::from_nanos(2_345_678_901);
126 let timeval = logical_timeval(now);
127 assert_eq!(timeval.tv_sec, 2);
128 assert_eq!(timeval.tv_usec, 345_678);
129 let timespec = logical_timespec(now);
130 assert_eq!(timespec.tv_sec, 2);
131 assert_eq!(timespec.tv_nsec, 345_678_901);
132 }
133}