shadow_rs/host/syscall/handler/
futex.rs1use linux_api::errno::Errno;
2use linux_api::futex::{FUTEX_BITSET_MATCH_ANY, FutexFlags, FutexOp, FutexOpAndFlags};
3use shadow_shim_helper_rs::emulated_time::EmulatedTime;
4use shadow_shim_helper_rs::simulation_time::SimulationTime;
5use shadow_shim_helper_rs::syscall_types::ForeignPtr;
6
7use crate::core::worker::Worker;
8use crate::cshadow as c;
9use crate::host::futex_table::FutexRef;
10use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
11use crate::host::syscall::type_formatting::SyscallNonDeterministicArg;
12use crate::host::syscall::types::SyscallError;
13
14impl SyscallHandler {
15 log_syscall!(
16 futex,
17 std::ffi::c_int,
18 *const u32,
19 FutexOpAndFlags,
20 u32,
21 *const std::ffi::c_void,
22 *const u32,
23 SyscallNonDeterministicArg<u32>,
24 );
25 pub fn futex(
26 ctx: &mut SyscallContext,
27 uaddr: ForeignPtr<u32>,
28 op: std::ffi::c_int,
29 val: u32,
30 utime: ForeignPtr<linux_api::time::kernel_timespec>,
31 _uaddr2: ForeignPtr<u32>,
32 val3: u32,
33 ) -> Result<std::ffi::c_int, SyscallError> {
34 let op = FutexOpAndFlags::try_from(op).map_err(|_| Errno::EINVAL)?;
40 log::trace!("futex called with addr={uaddr:p} op={op:?}) and val={val}",);
41
42 let options = op.flags();
43 let operation = op.op();
44
45 for option in options.iter() {
46 match option {
47 FutexFlags::FUTEX_PRIVATE_FLAG => {
48 }
55 FutexFlags::FUTEX_CLOCK_REALTIME => {
56 }
59 unhandled_flag => {
60 log::warn!("Unhandled futex flag: {unhandled_flag:?}");
63 }
64 }
65 }
66
67 match operation {
68 FutexOp::FUTEX_WAIT => {
69 log::trace!("Handling FUTEX_WAIT operation {operation:?}");
70 return Self::futex_wait_helper(ctx, uaddr, val, utime, TimeoutType::Relative);
71 }
72 FutexOp::FUTEX_WAKE => {
73 log::trace!("Handling FUTEX_WAKE operation {operation:?}");
74 return Ok(Self::futex_wake_helper(ctx, uaddr.cast::<()>(), val) as i32);
77 }
78 FutexOp::FUTEX_WAIT_BITSET => {
79 log::trace!("Handling FUTEX_WAIT_BITSET operation {operation:?} bitset {val3:b}");
80 if val3 == FUTEX_BITSET_MATCH_ANY {
81 return Self::futex_wait_helper(ctx, uaddr, val, utime, TimeoutType::Absolute);
82 }
83 }
85 FutexOp::FUTEX_WAKE_BITSET => {
86 log::trace!("Handling FUTEX_WAKE_BITSET operation {operation:?} bitset {val3:b}");
87 if val3 == FUTEX_BITSET_MATCH_ANY {
88 return Ok(Self::futex_wake_helper(ctx, uaddr.cast::<()>(), val) as i32);
91 }
92 }
94 _ => {}
95 }
96
97 log::warn!("Unhandled futex operation {operation:?}");
98 Err(Errno::ENOSYS.into())
99 }
100
101 fn futex_wake_helper(ctx: &mut SyscallContext, ptr: ForeignPtr<()>, num_wakeups: u32) -> u32 {
102 let ptr = ctx.objs.process.physical_address(ptr);
104
105 let table = ctx.objs.host.futextable_borrow();
107 let futex = table.get(ptr);
108
109 let Some(futex) = futex else {
110 log::trace!("No futex found at futex addr {ptr:p}");
111 return 0;
112 };
113
114 log::trace!("Found futex {:p} at futex addr {ptr:p}", futex.ptr());
115
116 if num_wakeups == 0 {
117 return 0;
118 }
119
120 log::trace!("Futex trying to perform {num_wakeups} wakeups");
121 let num_woken = futex.wake(num_wakeups);
122 log::trace!("Futex was able to perform {num_woken}/{num_wakeups} wakeups");
123
124 num_woken
125 }
126
127 fn futex_wait_helper(
128 ctx: &mut SyscallContext,
129 ptr: ForeignPtr<u32>,
130 expected_val: u32,
131 timeout: ForeignPtr<linux_api::time::kernel_timespec>,
132 timeout_type: TimeoutType,
133 ) -> Result<i32, SyscallError> {
134 let mem = ctx.objs.process.memory_borrow();
135
136 let timeout = if timeout.is_null() {
139 None
140 } else {
141 let tspec = mem.read(timeout)?;
142 let sim_time = SimulationTime::try_from(tspec).map_err(|_| Errno::EINVAL)?;
143 Some(sim_time)
144 };
145
146 let futex_val = mem.read(ptr)?;
150
151 log::trace!("Futex value is {futex_val}, expected value is {expected_val}");
152 if !ctx.handler.is_blocked() && futex_val != expected_val {
153 log::trace!("Futex values don't match, try again later");
154 return Err(Errno::EAGAIN.into());
155 }
156
157 let ptr = ctx.objs.process.physical_address(ptr.cast::<()>());
159
160 let mut table = ctx.objs.host.futextable_borrow_mut();
162 let futex = table.get(ptr);
163
164 if ctx.handler.is_blocked() {
165 let futex = futex.expect("syscall was blocked, but there wasn't an existing futex");
166
167 let result;
168
169 if timeout.is_some() && ctx.handler.did_listen_timeout_expire() {
171 log::trace!("Futex {ptr:p} timeout out while waiting");
173 result = Err(Errno::ETIMEDOUT);
174 } else if ctx.objs.thread.unblocked_signal_pending(
175 ctx.objs.process,
176 &ctx.objs.host.shim_shmem_lock_borrow().unwrap(),
177 ) {
178 log::trace!("Futex {ptr:p} has been interrupted by a signal");
179 result = Err(Errno::EINTR);
180 } else {
181 log::trace!("Futex {ptr:p} has been woken up");
183 result = Ok(0);
184 }
185
186 if futex.listener_count() == 0 {
188 log::trace!("Dynamically freed a futex object for futex addr {ptr:p}");
189 table.remove(ptr).expect("futex disappeared");
190 }
191
192 return result.map_err(Into::into);
193 }
194
195 let futex = match futex {
197 Some(x) => x.clone(),
198 None => {
199 log::trace!("Dynamically created a new futex object for futex addr {ptr:p}");
200
201 let futex = unsafe { c::futex_new(ptr) };
202 assert!(!futex.is_null());
203 let futex = unsafe { FutexRef::new(futex) };
204
205 table
206 .add(futex.clone())
207 .expect("new futex is already in table");
208
209 futex
210 }
211 };
212
213 log::trace!(
215 "Futex blocking for wakeup {} timeout",
216 if timeout.is_some() { "with" } else { "without" },
217 );
218 let mut rv = SyscallError::new_blocked_on_futex(futex, true);
219 if let Some(timeout) = timeout {
220 let now = Worker::current_time().unwrap();
221 let timeout = match timeout_type {
222 TimeoutType::Relative => now + timeout,
223 TimeoutType::Absolute => EmulatedTime::UNIX_EPOCH + timeout,
224 };
225
226 let timeout = std::cmp::max(timeout, now);
228
229 rv.blocked_condition().unwrap().set_timeout(Some(timeout));
230 }
231
232 Err(rv)
233 }
234
235 log_syscall!(
236 get_robust_list,
237 std::ffi::c_int,
238 std::ffi::c_int,
239 *const std::ffi::c_void,
240 *const libc::size_t,
241 );
242 pub fn get_robust_list(
243 _ctx: &mut SyscallContext,
244 _pid: std::ffi::c_int,
245 _head_ptr: ForeignPtr<ForeignPtr<linux_api::futex::robust_list_head>>,
246 _len_ptr: ForeignPtr<libc::size_t>,
247 ) -> Result<(), Errno> {
248 warn_once_then_debug!("get_robust_list was called but we don't yet support it");
249 Err(Errno::ENOSYS)
250 }
251
252 log_syscall!(
253 set_robust_list,
254 std::ffi::c_int,
255 *const std::ffi::c_void,
256 libc::size_t,
257 );
258 pub fn set_robust_list(
259 _ctx: &mut SyscallContext,
260 _head: ForeignPtr<linux_api::futex::robust_list_head>,
261 _len: libc::size_t,
262 ) -> Result<(), Errno> {
263 warn_once_then_debug!("set_robust_list was called but we don't yet support it");
264 Err(Errno::ENOSYS)
265 }
266}
267
268enum TimeoutType {
270 Relative,
272 Absolute,
274}