shadow_rs/host/syscall/handler/
epoll.rs1use std::ops::DerefMut;
2use std::sync::Arc;
3
4use linux_api::epoll::{EpollCreateFlags, EpollCtlOp, EpollEvents};
5use linux_api::errno::Errno;
6use linux_api::fcntl::DescriptorFlags;
7use shadow_shim_helper_rs::simulation_time::SimulationTime;
8use shadow_shim_helper_rs::syscall_types::ForeignPtr;
9
10use crate::core::worker::Worker;
11use crate::cshadow;
12use crate::host::descriptor::descriptor_table::DescriptorHandle;
13use crate::host::descriptor::epoll::Epoll;
14use crate::host::descriptor::{CompatFile, Descriptor, File, FileState, OpenFile};
15use crate::host::memory_manager::MemoryManager;
16use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
17use crate::host::syscall::type_formatting::SyscallArrayArg;
18use crate::host::syscall::types::{ForeignArrayPtr, SyscallError};
19use crate::utility::callback_queue::CallbackQueue;
20
21impl SyscallHandler {
22 log_syscall!(
23 epoll_create,
24 std::ffi::c_int,
25 std::ffi::c_int,
26 );
27 pub fn epoll_create(
28 ctx: &mut SyscallContext,
29 size: std::ffi::c_int,
30 ) -> Result<DescriptorHandle, Errno> {
31 if size <= 0 {
34 return Err(Errno::EINVAL);
35 }
36
37 Self::epoll_create_helper(ctx, 0)
38 }
39
40 log_syscall!(
41 epoll_create1,
42 std::ffi::c_int,
43 std::ffi::c_int,
44 );
45 pub fn epoll_create1(
46 ctx: &mut SyscallContext,
47 flags: std::ffi::c_int,
48 ) -> Result<DescriptorHandle, Errno> {
49 Self::epoll_create_helper(ctx, flags)
50 }
51
52 fn epoll_create_helper(
53 ctx: &mut SyscallContext,
54 flags: std::ffi::c_int,
55 ) -> Result<DescriptorHandle, Errno> {
56 let Some(flags) = EpollCreateFlags::from_bits(flags) else {
59 log::debug!("Invalid epoll_create flags: {flags}");
60 return Err(Errno::EINVAL);
61 };
62
63 let mut desc_flags = DescriptorFlags::empty();
64
65 if flags.contains(EpollCreateFlags::EPOLL_CLOEXEC) {
66 desc_flags.insert(DescriptorFlags::FD_CLOEXEC);
67 }
68
69 let epoll = Epoll::new();
70 let mut desc = Descriptor::new(CompatFile::New(OpenFile::new(File::Epoll(epoll))));
71 desc.set_flags(desc_flags);
72
73 let fd = ctx
74 .objs
75 .thread
76 .descriptor_table_borrow_mut(ctx.objs.host)
77 .register_descriptor(desc)
78 .or(Err(Errno::ENFILE))?;
79
80 log::trace!("Created epoll fd {fd}");
81
82 Ok(fd)
83 }
84
85 log_syscall!(
86 epoll_ctl,
87 std::ffi::c_int,
88 std::ffi::c_int,
89 std::ffi::c_int,
90 std::ffi::c_int,
91 *const std::ffi::c_void,
92 );
93 pub fn epoll_ctl(
94 ctx: &mut SyscallContext,
95 epfd: std::ffi::c_int,
96 op: std::ffi::c_int,
97 fd: std::ffi::c_int,
98 event_ptr: ForeignPtr<linux_api::epoll::epoll_event>,
99 ) -> Result<(), Errno> {
100 let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
105
106 let (epoll, epoll_canon_handle) = {
108 let desc = Self::get_descriptor(&desc_table, epfd)?;
109
110 let CompatFile::New(epoll) = desc.file() else {
111 return Err(Errno::EINVAL);
112 };
113
114 let epoll_canon_handle = epoll.inner_file().canonical_handle();
115
116 let File::Epoll(epoll) = epoll.inner_file() else {
117 return Err(Errno::EINVAL);
118 };
119
120 (epoll, epoll_canon_handle)
121 };
122
123 let target = {
125 let desc = Self::get_descriptor(&desc_table, fd)?;
126
127 match desc.file() {
132 CompatFile::New(file) => file.inner_file().clone(),
133 CompatFile::Legacy(file) => {
134 let file_type = unsafe { cshadow::legacyfile_getType(file.ptr()) };
135 if file_type == cshadow::_LegacyFileType_DT_FILE {
136 return Err(Errno::EPERM);
138 } else {
139 warn_once_then_trace!(
142 "Attempted to add a legacy file to an epoll file, which \
143 shadow doesn't support"
144 );
145 return Err(Errno::EINVAL);
146 }
147 }
148 }
149 };
150
151 if epoll_canon_handle == target.canonical_handle() {
153 return Err(Errno::EINVAL);
154 }
155
156 let Ok(op) = EpollCtlOp::try_from(op) else {
158 log::debug!("Invalid epoll op: {op}");
159 return Err(Errno::EINVAL);
160 };
161
162 let (events, data) = if op == EpollCtlOp::EPOLL_CTL_DEL {
164 (EpollEvents::empty(), 0)
167 } else {
168 let mem = ctx.objs.process.memory_borrow();
169 let ev = mem.read(event_ptr)?;
170
171 let Some(mut events) = EpollEvents::from_bits(ev.events) else {
172 log::debug!("Invalid epoll_ctl events: {}", { ev.events });
174 return Err(Errno::EINVAL);
175 };
176
177 events.insert(EpollEvents::EPOLLERR | EpollEvents::EPOLLHUP);
179
180 (events, ev.data)
181 };
182
183 log::trace!("Calling epoll_ctl on epoll {epfd} with child {fd}");
184
185 CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
186 let weak_epoll = Arc::downgrade(epoll);
187 epoll
188 .borrow_mut()
189 .ctl(op, fd, target, events, data, weak_epoll, cb_queue)
190 })?;
191 Ok(())
192 }
193
194 log_syscall!(
195 epoll_wait,
196 std::ffi::c_int,
197 std::ffi::c_int,
198 SyscallArrayArg<2, linux_api::epoll::epoll_event>,
199 std::ffi::c_int,
200 std::ffi::c_int,
201 );
202 pub fn epoll_wait(
203 ctx: &mut SyscallContext,
204 epfd: std::ffi::c_int,
205 events_ptr: ForeignPtr<linux_api::epoll::epoll_event>,
206 max_events: std::ffi::c_int,
207 timeout: std::ffi::c_int,
208 ) -> Result<std::ffi::c_int, SyscallError> {
209 let timeout = timeout_arg_to_maybe_simtime(timeout)?;
211 Self::epoll_wait_helper(ctx, epfd, events_ptr, max_events, timeout, None)
212 }
213
214 log_syscall!(
215 epoll_pwait,
216 std::ffi::c_int,
217 std::ffi::c_int,
218 SyscallArrayArg<2, linux_api::epoll::epoll_event>,
219 std::ffi::c_int,
220 std::ffi::c_int,
221 *const std::ffi::c_void,
222 linux_api::posix_types::kernel_size_t,
223 );
224 pub fn epoll_pwait(
225 ctx: &mut SyscallContext,
226 epfd: std::ffi::c_int,
227 events_ptr: ForeignPtr<linux_api::epoll::epoll_event>,
228 max_events: std::ffi::c_int,
229 timeout: std::ffi::c_int,
230 sigmask_ptr: ForeignPtr<linux_api::signal::sigset_t>,
231 _sigsetsize: linux_api::posix_types::kernel_size_t,
232 ) -> Result<std::ffi::c_int, SyscallError> {
233 let sigmask = if sigmask_ptr.is_null() {
236 None
237 } else {
238 Some(ctx.objs.process.memory_borrow().read(sigmask_ptr)?)
239 };
240
241 let timeout = timeout_arg_to_maybe_simtime(timeout)?;
243 Self::epoll_wait_helper(ctx, epfd, events_ptr, max_events, timeout, sigmask)
244 }
245
246 log_syscall!(
247 epoll_pwait2,
248 std::ffi::c_int,
249 std::ffi::c_int,
250 SyscallArrayArg<2, linux_api::epoll::epoll_event>,
251 std::ffi::c_int,
252 *const std::ffi::c_void,
253 *const std::ffi::c_void,
254 linux_api::posix_types::kernel_size_t,
255 );
256 pub fn epoll_pwait2(
257 ctx: &mut SyscallContext,
258 epfd: std::ffi::c_int,
259 events_ptr: ForeignPtr<linux_api::epoll::epoll_event>,
260 max_events: std::ffi::c_int,
261 timeout_ptr: ForeignPtr<linux_api::time::timespec>,
262 sigmask_ptr: ForeignPtr<linux_api::signal::sigset_t>,
263 _sigsetsize: linux_api::posix_types::kernel_size_t,
264 ) -> Result<std::ffi::c_int, SyscallError> {
265 let (sigmask, timeout) = {
266 let mem = ctx.objs.process.memory_borrow();
267
268 let sigmask = if sigmask_ptr.is_null() {
271 None
272 } else {
273 Some(mem.read(sigmask_ptr)?)
274 };
275
276 let timeout = if timeout_ptr.is_null() {
278 None
279 } else {
280 let tspec = mem.read(timeout_ptr)?;
281 let sim_time = SimulationTime::try_from(tspec).map_err(|_| Errno::EINVAL)?;
282 Some(sim_time)
283 };
284
285 (sigmask, timeout)
286 };
287
288 Self::epoll_wait_helper(ctx, epfd, events_ptr, max_events, timeout, sigmask)
289 }
290
291 fn epoll_wait_helper(
292 ctx: &mut SyscallContext,
293 epfd: std::ffi::c_int,
294 events_ptr: ForeignPtr<linux_api::epoll::epoll_event>,
295 max_events: std::ffi::c_int,
296 timeout: Option<SimulationTime>,
297 sigmask: Option<linux_api::signal::sigset_t>,
298 ) -> Result<std::ffi::c_int, SyscallError> {
299 let max_events = {
301 let upper_bound = epoll_max_events_upper_bound();
302
303 if max_events <= 0 || max_events > upper_bound {
304 log::trace!(
305 "Epoll maxevents {max_events} is not greater than 0 \
306 and less than {upper_bound}"
307 );
308 return Err(Errno::EINVAL.into());
309 }
310
311 u32::try_from(max_events).unwrap()
312 };
313
314 if sigmask.is_some() {
316 warn_once_then_trace!(
317 "Epoll pwait called with non-null sigmask, \
318 which is not yet supported by shadow; returning EINVAL"
319 );
320 return Err(Errno::EINVAL.into());
321 }
322
323 let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
325 let epoll = {
326 let desc = Self::get_descriptor(&desc_table, epfd)?;
327
328 let CompatFile::New(epoll) = desc.file() else {
329 return Err(Errno::EINVAL.into());
330 };
331
332 let File::Epoll(epoll) = epoll.inner_file() else {
333 return Err(Errno::EINVAL.into());
334 };
335
336 epoll
337 };
338
339 if epoll.borrow().has_ready_events() {
340 log::trace!("Epoll {epfd} has ready events");
341
342 if events_ptr.is_null() {
347 return Err(Errno::EFAULT.into());
348 }
349
350 let ready = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
354 epoll
355 .borrow_mut()
356 .collect_ready_events(cb_queue, max_events)
357 });
358 let n_ready = ready.len();
359 if n_ready > max_events as usize {
360 panic!("Epoll should not return more than {max_events} events");
361 }
362
363 let mut mem = ctx.objs.process.memory_borrow_mut();
365 write_events_to_ptr(&mut mem, ready, events_ptr)?;
366
367 log::trace!("Epoll {epfd} returning {n_ready} events");
369 return Ok(n_ready.try_into().unwrap());
370 }
371
372 if let Some(timeout) = timeout
375 && timeout.is_zero()
376 {
377 log::trace!("No events are ready on epoll {epfd} and the timeout is 0");
378 return Ok(0);
379 }
380
381 if let Some(cond) = ctx.objs.thread.syscall_condition()
384 && let Some(abs_timeout) = cond.timeout()
385 && Worker::current_time().unwrap() >= abs_timeout
386 {
387 log::trace!("No events are ready on epoll {epfd} and the timeout expired");
388 return Ok(0);
389 }
390
391 if ctx.objs.thread.unblocked_signal_pending(
393 ctx.objs.process,
394 &ctx.objs.host.shim_shmem_lock_borrow().unwrap(),
395 ) {
396 return Err(SyscallError::new_interrupted(false));
397 }
398
399 let Ok(abs_timeout_opt) = timeout
401 .map(|x| Worker::current_time().unwrap().checked_add(x).ok_or(()))
402 .transpose()
403 else {
404 log::trace!("Epoll wait with invalid timeout {timeout:?} (too large)");
405 return Err(Errno::EINVAL.into());
406 };
407
408 log::trace!("No events are ready on epoll {epfd} and we need to block");
409
410 let mut rv = SyscallError::new_blocked_on_file(
412 File::Epoll(Arc::clone(epoll)),
413 FileState::READABLE,
414 false,
415 );
416
417 if abs_timeout_opt.is_some() {
419 rv.blocked_condition().unwrap().set_timeout(abs_timeout_opt);
420 }
421
422 Err(rv)
423 }
424}
425
426fn timeout_arg_to_maybe_simtime(
427 timeout_ms: std::ffi::c_int,
428) -> Result<Option<SimulationTime>, Errno> {
429 let timeout_ms = (timeout_ms >= 0).then_some(timeout_ms);
431
432 if let Some(timeout_ms) = timeout_ms {
433 let timeout_ms = timeout_ms.try_into().unwrap();
435 let timeout = SimulationTime::try_from_millis(timeout_ms).ok_or(Errno::EINVAL)?;
436 Ok(Some(timeout))
437 } else {
438 Ok(None)
439 }
440}
441
442fn epoll_max_events_upper_bound() -> i32 {
451 let ep_max_events = i32::MAX;
452 let ep_ev_size: i32 = std::mem::size_of::<linux_api::epoll::epoll_event>()
453 .try_into()
454 .unwrap_or(i32::MAX);
455 ep_max_events.saturating_div(ep_ev_size)
456}
457
458fn write_events_to_ptr(
459 mem: &mut MemoryManager,
460 ready: Vec<(EpollEvents, u64)>,
461 events_ptr: ForeignPtr<linux_api::epoll::epoll_event>,
462) -> Result<(), Errno> {
463 let events_ptr = ForeignArrayPtr::new(events_ptr, ready.len());
464 let mut mem_ref = mem.memory_ref_mut(events_ptr)?;
465
466 for ((ev, data), plugin_ev) in ready.iter().zip(mem_ref.deref_mut().iter_mut()) {
467 plugin_ev.events = ev.bits();
468 plugin_ev.data = *data;
469 }
470
471 mem_ref.flush()?;
472
473 Ok(())
474}