miri/shims/unix/linux_like/
epoll.rs1use std::cell::RefCell;
2use std::collections::{BTreeMap, BTreeSet, VecDeque};
3use std::io;
4use std::ops::Bound;
5use std::time::Duration;
6
7use rustc_abi::FieldIdx;
8
9use crate::concurrency::VClock;
10use crate::shims::files::{
11 DynFileDescriptionRef, FdId, FdNum, FileDescription, FileDescriptionRef, WeakFileDescriptionRef,
12};
13use crate::shims::unix::UnixFileDescription;
14use crate::*;
15
16type EpollEventKey = (FdId, FdNum);
17
18#[derive(Debug, Default)]
20struct Epoll {
21 interest_list: RefCell<BTreeMap<EpollEventKey, EpollEventInterest>>,
24 ready_set: RefCell<BTreeSet<EpollEventKey>>,
27 queue: RefCell<VecDeque<ThreadId>>,
29}
30
31impl VisitProvenance for Epoll {
32 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
33 }
35}
36
37fn range_for_id(id: FdId) -> std::ops::RangeInclusive<EpollEventKey> {
39 (id, 0)..=(id, i32::MAX)
40}
41
42#[derive(Debug)]
44pub struct EpollEventInterest {
45 relevant_events: u32,
47 active_events: u32,
49 clock: VClock,
51 data: u64,
56}
57
58#[derive(Debug)]
60pub struct EpollEvents {
61 pub epollin: bool,
64 pub epollout: bool,
66 pub epollrdhup: bool,
69 pub epollhup: bool,
75 pub epollerr: bool,
77}
78
79impl EpollEvents {
80 pub fn new() -> Self {
81 EpollEvents {
82 epollin: false,
83 epollout: false,
84 epollrdhup: false,
85 epollhup: false,
86 epollerr: false,
87 }
88 }
89
90 pub fn get_event_bitmask<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> u32 {
91 let epollin = ecx.eval_libc_u32("EPOLLIN");
92 let epollout = ecx.eval_libc_u32("EPOLLOUT");
93 let epollrdhup = ecx.eval_libc_u32("EPOLLRDHUP");
94 let epollhup = ecx.eval_libc_u32("EPOLLHUP");
95 let epollerr = ecx.eval_libc_u32("EPOLLERR");
96
97 let mut bitmask = 0;
98 if self.epollin {
99 bitmask |= epollin;
100 }
101 if self.epollout {
102 bitmask |= epollout;
103 }
104 if self.epollrdhup {
105 bitmask |= epollrdhup;
106 }
107 if self.epollhup {
108 bitmask |= epollhup;
109 }
110 if self.epollerr {
111 bitmask |= epollerr;
112 }
113 bitmask
114 }
115}
116
117impl FileDescription for Epoll {
118 fn name(&self) -> &'static str {
119 "epoll"
120 }
121
122 fn metadata<'tcx>(
123 &self,
124 ) -> InterpResult<'tcx, Either<io::Result<std::fs::Metadata>, &'static str>> {
125 interp_ok(Either::Right("S_IFREG"))
127 }
128
129 fn destroy<'tcx>(
130 mut self,
131 self_id: FdId,
132 _communicate_allowed: bool,
133 ecx: &mut MiriInterpCx<'tcx>,
134 ) -> InterpResult<'tcx, io::Result<()>> {
135 let mut ids = self.interest_list.get_mut().keys().map(|(id, _num)| *id).collect::<Vec<_>>();
137 ids.dedup(); for id in ids {
139 ecx.machine.epoll_interests.remove(id, self_id);
140 }
141 interp_ok(Ok(()))
142 }
143
144 fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
145 self
146 }
147}
148
149impl UnixFileDescription for Epoll {}
150
151pub struct EpollInterestTable(BTreeMap<FdId, Vec<(FdId, WeakFileDescriptionRef<Epoll>)>>);
156
157impl EpollInterestTable {
158 pub(crate) fn new() -> Self {
159 EpollInterestTable(BTreeMap::new())
160 }
161
162 fn insert(&mut self, id: FdId, epoll: &FileDescriptionRef<Epoll>) {
163 let epolls = self.0.entry(id).or_default();
164 let idx = epolls
165 .binary_search_by_key(&epoll.id(), |&(id, _)| id)
166 .expect_err("trying to add an epoll that's already in the list");
167 epolls.insert(idx, (epoll.id(), FileDescriptionRef::downgrade(epoll)));
168 }
169
170 fn remove(&mut self, id: FdId, epoll_id: FdId) {
171 let epolls = self.0.entry(id).or_default();
172 let idx = epolls
173 .binary_search_by_key(&epoll_id, |&(id, _)| id)
174 .expect("trying to remove an epoll that's not in the list");
175 epolls.remove(idx);
176 }
177
178 fn get_epolls(&self, id: FdId) -> Option<impl Iterator<Item = &WeakFileDescriptionRef<Epoll>>> {
179 self.0.get(&id).map(|epolls| epolls.iter().map(|(_id, epoll)| epoll))
180 }
181
182 pub fn remove_epolls(&mut self, id: FdId) {
183 if let Some(epolls) = self.0.remove(&id) {
184 for epoll in epolls.iter().filter_map(|(_id, epoll)| epoll.upgrade()) {
185 epoll
188 .interest_list
189 .borrow_mut()
190 .extract_if(range_for_id(id), |_, _| true)
191 .for_each(drop);
193 epoll
194 .ready_set
195 .borrow_mut()
196 .extract_if(range_for_id(id), |_| true)
197 .for_each(drop);
199 }
200 }
201 }
202}
203
204impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
205pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
206 fn epoll_create1(&mut self, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
212 let this = self.eval_context_mut();
213
214 let flags = this.read_scalar(flags)?.to_i32()?;
215
216 let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC");
217
218 if flags != epoll_cloexec && flags != 0 {
220 throw_unsup_format!(
221 "epoll_create1: flag {:#x} is unsupported, only 0 or EPOLL_CLOEXEC are allowed",
222 flags
223 );
224 }
225
226 let fd = this.machine.fds.insert_new(Epoll::default());
227 interp_ok(Scalar::from_i32(fd))
228 }
229
230 fn epoll_ctl(
244 &mut self,
245 epfd: &OpTy<'tcx>,
246 op: &OpTy<'tcx>,
247 fd: &OpTy<'tcx>,
248 event: &OpTy<'tcx>,
249 ) -> InterpResult<'tcx, Scalar> {
250 let this = self.eval_context_mut();
251
252 let epfd_value = this.read_scalar(epfd)?.to_i32()?;
253 let op = this.read_scalar(op)?.to_i32()?;
254 let fd = this.read_scalar(fd)?.to_i32()?;
255 let event = this.deref_pointer_as(event, this.libc_ty_layout("epoll_event"))?;
256
257 let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD");
258 let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD");
259 let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL");
260 let epollin = this.eval_libc_u32("EPOLLIN");
261 let epollout = this.eval_libc_u32("EPOLLOUT");
262 let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
263 let epollet = this.eval_libc_u32("EPOLLET");
264 let epollhup = this.eval_libc_u32("EPOLLHUP");
265 let epollerr = this.eval_libc_u32("EPOLLERR");
266
267 if epfd_value == fd {
269 return this.set_last_error_and_return_i32(LibcError("EFAULT"));
270 }
271
272 let Some(epfd) = this.machine.fds.get(epfd_value) else {
274 return this.set_last_error_and_return_i32(LibcError("EBADF"));
275 };
276 let epfd = epfd
277 .downcast::<Epoll>()
278 .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
279
280 let mut interest_list = epfd.interest_list.borrow_mut();
281
282 let Some(fd_ref) = this.machine.fds.get(fd) else {
283 return this.set_last_error_and_return_i32(LibcError("EBADF"));
284 };
285 let id = fd_ref.id();
286
287 if op == epoll_ctl_add || op == epoll_ctl_mod {
288 let mut events =
290 this.read_scalar(&this.project_field(&event, FieldIdx::ZERO)?)?.to_u32()?;
291 let data = this.read_scalar(&this.project_field(&event, FieldIdx::ONE)?)?.to_u64()?;
292
293 let mut flags = events;
295 events |= epollhup;
299 events |= epollerr;
300
301 if events & epollet != epollet {
302 throw_unsup_format!("epoll_ctl: epollet flag must be included.");
304 } else {
305 flags &= !epollet;
306 }
307 if flags & epollin == epollin {
308 flags &= !epollin;
309 }
310 if flags & epollout == epollout {
311 flags &= !epollout;
312 }
313 if flags & epollrdhup == epollrdhup {
314 flags &= !epollrdhup;
315 }
316 if flags & epollhup == epollhup {
317 flags &= !epollhup;
318 }
319 if flags & epollerr == epollerr {
320 flags &= !epollerr;
321 }
322 if flags != 0 {
323 throw_unsup_format!(
324 "epoll_ctl: encountered unknown unsupported flags {:#x}",
325 flags
326 );
327 }
328
329 let epoll_key = (id, fd);
332 if op == epoll_ctl_add {
333 if interest_list.range(range_for_id(id)).next().is_none() {
334 this.machine.epoll_interests.insert(id, &epfd);
337 }
338 let new_interest = EpollEventInterest {
339 relevant_events: events,
340 data,
341 active_events: 0,
342 clock: VClock::default(),
343 };
344 if interest_list.try_insert(epoll_key, new_interest).is_err() {
345 return this.set_last_error_and_return_i32(LibcError("EEXIST"));
347 }
348 } else {
349 let Some(interest) = interest_list.get_mut(&epoll_key) else {
351 return this.set_last_error_and_return_i32(LibcError("ENOENT"));
352 };
353 interest.relevant_events = events;
354 interest.data = data;
355 }
356
357 update_readiness(
359 this,
360 &epfd,
361 fd_ref.as_unix(this).epoll_active_events()?.get_event_bitmask(this),
362 true,
363 move |callback| {
364 callback(epoll_key, interest_list.get_mut(&epoll_key).unwrap())
367 },
368 )?;
369
370 interp_ok(Scalar::from_i32(0))
371 } else if op == epoll_ctl_del {
372 let epoll_key = (id, fd);
373
374 if interest_list.remove(&epoll_key).is_none() {
376 return this.set_last_error_and_return_i32(LibcError("ENOENT"));
378 };
379 epfd.ready_set.borrow_mut().remove(&epoll_key);
380 if interest_list.range(range_for_id(id)).next().is_none() {
383 this.machine.epoll_interests.remove(id, epfd.id());
384 }
385
386 interp_ok(Scalar::from_i32(0))
387 } else {
388 throw_unsup_format!("unsupported epoll_ctl operation: {op}");
389 }
390 }
391
392 fn epoll_wait(
425 &mut self,
426 epfd: &OpTy<'tcx>,
427 events_op: &OpTy<'tcx>,
428 maxevents: &OpTy<'tcx>,
429 timeout: &OpTy<'tcx>,
430 dest: &MPlaceTy<'tcx>,
431 ) -> InterpResult<'tcx> {
432 let this = self.eval_context_mut();
433
434 let epfd_value = this.read_scalar(epfd)?.to_i32()?;
435 let events = this.read_immediate(events_op)?;
436 let maxevents = this.read_scalar(maxevents)?.to_i32()?;
437 let timeout = this.read_scalar(timeout)?.to_i32()?;
438
439 if epfd_value <= 0 || maxevents <= 0 {
440 return this.set_last_error_and_return(LibcError("EINVAL"), dest);
441 }
442
443 let event = this.deref_pointer_as(
446 &events,
447 this.libc_array_ty_layout("epoll_event", maxevents.try_into().unwrap()),
448 )?;
449
450 let Some(epfd) = this.machine.fds.get(epfd_value) else {
451 return this.set_last_error_and_return(LibcError("EBADF"), dest);
452 };
453 let Some(epfd) = epfd.downcast::<Epoll>() else {
454 return this.set_last_error_and_return(LibcError("EBADF"), dest);
455 };
456
457 if timeout == 0 || !epfd.ready_set.borrow().is_empty() {
458 return_ready_list(&epfd, dest, &event, this)?;
460 } else {
461 let timeout = match timeout {
463 0.. => {
464 let duration = Duration::from_millis(timeout.try_into().unwrap());
465 Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration))
466 }
467 -1 => None,
468 ..-1 => {
469 throw_unsup_format!(
470 "epoll_wait: Only timeout values greater than or equal to -1 are supported."
471 );
472 }
473 };
474 epfd.queue.borrow_mut().push_back(this.active_thread());
476 let dest = dest.clone();
478 this.block_thread(
482 BlockReason::Epoll,
483 timeout,
484 callback!(
485 @capture<'tcx> {
486 epfd: FileDescriptionRef<Epoll>,
487 dest: MPlaceTy<'tcx>,
488 event: MPlaceTy<'tcx>,
489 }
490 |this, unblock: UnblockKind| {
491 match unblock {
492 UnblockKind::Ready => {
493 let events = return_ready_list(&epfd, &dest, &event, this)?;
494 assert!(events > 0, "we got woken up with no events to deliver");
495 interp_ok(())
496 },
497 UnblockKind::TimedOut => {
498 epfd
500 .queue.borrow_mut()
501 .retain(|&id| id != this.active_thread());
502 this.write_int(0, &dest)?;
503 interp_ok(())
504 },
505 }
506 }
507 ),
508 );
509 }
510 interp_ok(())
511 }
512
513 fn update_epoll_active_events(
520 &mut self,
521 fd_ref: DynFileDescriptionRef,
522 force_edge: bool,
523 ) -> InterpResult<'tcx> {
524 let this = self.eval_context_mut();
525 let id = fd_ref.id();
526 let Some(epolls) = this.machine.epoll_interests.get_epolls(id) else {
529 return interp_ok(());
530 };
531 let epolls = epolls
532 .map(|weak| {
533 weak.upgrade()
534 .expect("someone forgot to remove the garbage from `machine.epoll_interests`")
535 })
536 .collect::<Vec<_>>();
537 let active_events = fd_ref.as_unix(this).epoll_active_events()?.get_event_bitmask(this);
538 for epoll in epolls {
539 update_readiness(this, &epoll, active_events, force_edge, |callback| {
540 for (&key, interest) in epoll.interest_list.borrow_mut().range_mut(range_for_id(id))
541 {
542 callback(key, interest)?;
543 }
544 interp_ok(())
545 })?;
546 }
547
548 interp_ok(())
549 }
550}
551
552fn update_readiness<'tcx>(
559 ecx: &mut MiriInterpCx<'tcx>,
560 epoll: &Epoll,
561 active_events: u32,
562 force_edge: bool,
563 for_each_interest: impl FnOnce(
564 &mut dyn FnMut(EpollEventKey, &mut EpollEventInterest) -> InterpResult<'tcx>,
565 ) -> InterpResult<'tcx>,
566) -> InterpResult<'tcx> {
567 let mut ready_set = epoll.ready_set.borrow_mut();
568 for_each_interest(&mut |key, interest| {
569 let new_readiness = interest.relevant_events & active_events;
571 let prev_readiness = std::mem::replace(&mut interest.active_events, new_readiness);
572 if new_readiness == 0 {
573 ready_set.remove(&key);
575 } else if force_edge || new_readiness != prev_readiness & new_readiness {
576 ready_set.insert(key);
579 ecx.release_clock(|clock| {
580 interest.clock.join(clock);
581 })?;
582 }
583 interp_ok(())
584 })?;
585 while !ready_set.is_empty()
587 && let Some(thread_id) = epoll.queue.borrow_mut().pop_front()
588 {
589 drop(ready_set); ecx.unblock_thread(thread_id, BlockReason::Epoll)?;
591 ready_set = epoll.ready_set.borrow_mut();
592 }
593
594 interp_ok(())
595}
596
597fn return_ready_list<'tcx>(
600 epfd: &FileDescriptionRef<Epoll>,
601 dest: &MPlaceTy<'tcx>,
602 events: &MPlaceTy<'tcx>,
603 ecx: &mut MiriInterpCx<'tcx>,
604) -> InterpResult<'tcx, i32> {
605 let mut interest_list = epfd.interest_list.borrow_mut();
606 let mut ready_set = epfd.ready_set.borrow_mut();
607 let mut num_of_events: i32 = 0;
608 let mut array_iter = ecx.project_array_fields(events)?;
609
610 if cfg!(debug_assertions) {
612 for (key, interest) in interest_list.iter() {
613 let fd = &ecx.machine.fds.fds.values().find(|fd| fd.id() == key.0).unwrap();
616 let current_active = fd.as_unix(ecx).epoll_active_events()?.get_event_bitmask(ecx);
617 assert_eq!(interest.active_events, current_active & interest.relevant_events);
618 }
619 }
620
621 let mut event_lower_bound = Bound::Unbounded;
626 while let Some(slot) = array_iter.next(ecx)?
627 && let Some(&key) = ready_set.range((event_lower_bound, Bound::Unbounded)).next()
628 {
629 let interest = interest_list.get_mut(&key).expect("non-existent event in ready set");
630 ecx.write_int_fields_named(
632 &[("events", interest.active_events.into()), ("u64", interest.data.into())],
633 &slot.1,
634 )?;
635 num_of_events = num_of_events.strict_add(1);
636 ecx.acquire_clock(&interest.clock)?;
638 ready_set.remove(&key);
640 event_lower_bound = Bound::Excluded(key);
642 }
643 ecx.write_int(num_of_events, dest)?;
644 interp_ok(num_of_events)
645}