miri/shims/unix/linux_like/
epoll.rs1use std::io;
2use std::rc::Rc;
3use std::time::Duration;
4
5use rustc_abi::FieldIdx;
6
7use crate::shims::files::{FileDescription, FileDescriptionRef};
8use crate::shims::unix::UnixFileDescription;
9use crate::*;
10
11#[derive(Debug)]
13pub struct Epoll {
14 watcher: Rc<ReadinessWatcher>,
17}
18
19impl FileDescription for Epoll {
20 fn name(&self) -> &'static str {
21 "epoll"
22 }
23
24 fn metadata<'tcx>(
25 &self,
26 ) -> InterpResult<'tcx, Either<io::Result<std::fs::Metadata>, &'static str>> {
27 interp_ok(Either::Right("S_IFREG"))
29 }
30
31 fn as_unix(self: FileDescriptionRef<Self>) -> FileDescriptionRef<dyn UnixFileDescription> {
32 self
33 }
34}
35
36impl UnixFileDescription for Epoll {}
37
38impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
39pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
40 fn epoll_create1(&mut self, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
46 let this = self.eval_context_mut();
47
48 let flags = this.read_scalar(flags)?.to_i32()?;
49
50 let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC");
51
52 if flags != epoll_cloexec && flags != 0 {
54 throw_unsup_format!(
55 "epoll_create1: flag {:#x} is unsupported, only 0 or EPOLL_CLOEXEC are allowed",
56 flags
57 );
58 }
59
60 let fd =
61 this.machine.fds.insert_new(Epoll { watcher: Rc::new(ReadinessWatcher::default()) });
62 interp_ok(Scalar::from_i32(fd))
63 }
64
65 fn epoll_ctl(
79 &mut self,
80 epfd: &OpTy<'tcx>,
81 op: &OpTy<'tcx>,
82 fd: &OpTy<'tcx>,
83 event: &OpTy<'tcx>,
84 ) -> InterpResult<'tcx, Scalar> {
85 let this = self.eval_context_mut();
86
87 let epfd_value = this.read_scalar(epfd)?.to_i32()?;
88 let op = this.read_scalar(op)?.to_i32()?;
89 let fd = this.read_scalar(fd)?.to_i32()?;
90 let event = this.deref_pointer_as(event, this.libc_ty_layout("epoll_event"))?;
91
92 let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD");
93 let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD");
94 let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL");
95 let epollin = this.eval_libc_u32("EPOLLIN");
96 let epollout = this.eval_libc_u32("EPOLLOUT");
97 let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
98 let epollet = this.eval_libc_u32("EPOLLET");
99 let epollhup = this.eval_libc_u32("EPOLLHUP");
100 let epollerr = this.eval_libc_u32("EPOLLERR");
101
102 if epfd_value == fd {
104 return this.set_errno_and_return_neg1_i32(LibcError("EFAULT"));
105 }
106
107 let Some(epfd) = this.machine.fds.get(epfd_value) else {
109 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
110 };
111 let epfd = epfd
112 .downcast::<Epoll>()
113 .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
114
115 let Some(fd_ref) = this.machine.fds.get(fd) else {
116 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
117 };
118 let id = fd_ref.id();
119 let interest_key = (id, fd);
120
121 if op == epoll_ctl_add || op == epoll_ctl_mod {
122 let mut relevant_bitflag =
124 this.read_scalar(&this.project_field(&event, FieldIdx::ZERO)?)?.to_u32()?;
125 let data = this.read_scalar(&this.project_field(&event, FieldIdx::ONE)?)?.to_u64()?;
126
127 let is_edge_triggered = if relevant_bitflag & epollet == epollet {
128 relevant_bitflag &= !epollet;
129 true
130 } else {
131 false
132 };
133
134 let mut flags = relevant_bitflag;
136 relevant_bitflag |= epollhup;
140 relevant_bitflag |= epollerr;
141
142 if flags & epollin == epollin {
143 flags &= !epollin;
144 }
145 if flags & epollout == epollout {
146 flags &= !epollout;
147 }
148 if flags & epollrdhup == epollrdhup {
149 flags &= !epollrdhup;
150 }
151 if flags & epollhup == epollhup {
152 flags &= !epollhup;
153 }
154 if flags & epollerr == epollerr {
155 flags &= !epollerr;
156 }
157 if flags != 0 {
158 throw_unsup_format!(
159 "epoll_ctl: encountered unknown unsupported flags {:#x}",
160 flags
161 );
162 }
163
164 let relevant = this.epoll_bitflag_to_readiness(relevant_bitflag);
165
166 if op == epoll_ctl_add {
167 let result =
169 epfd.watcher.add_interest(fd, relevant, is_edge_triggered, data, this)?;
170 if result.is_err() {
171 return this.set_errno_and_return_neg1_i32(LibcError("EEXIST"));
173 }
174 } else {
175 let result = epfd.watcher.update_interest(interest_key, this, |interest| {
177 interest.is_edge_triggered = is_edge_triggered;
178 interest.relevant = relevant;
179 interest.data = data;
180 })?;
181 if result.is_none() {
182 return this.set_errno_and_return_neg1_i32(LibcError("ENOENT"));
184 }
185 }
186 } else if op == epoll_ctl_del {
187 if epfd.watcher.remove_interest(interest_key).is_none() {
188 return this.set_errno_and_return_neg1_i32(LibcError("ENOENT"));
190 };
191 } else {
192 throw_unsup_format!("unsupported epoll_ctl operation: {op}");
193 }
194
195 interp_ok(Scalar::from_i32(0))
196 }
197
198 fn epoll_wait(
231 &mut self,
232 epfd: &OpTy<'tcx>,
233 events_op: &OpTy<'tcx>,
234 maxevents: &OpTy<'tcx>,
235 timeout: &OpTy<'tcx>,
236 dest: &MPlaceTy<'tcx>,
237 ) -> InterpResult<'tcx> {
238 let this = self.eval_context_mut();
239
240 let epfd_value = this.read_scalar(epfd)?.to_i32()?;
241 let events = this.read_immediate(events_op)?;
242 let maxevents = this.read_scalar(maxevents)?.to_i32()?;
243 let timeout = this.read_scalar(timeout)?.to_i32()?;
244
245 if epfd_value <= 0 || maxevents <= 0 {
246 return this.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
247 }
248
249 let event = this.deref_pointer_as(
252 &events,
253 this.libc_array_ty_layout("epoll_event", maxevents.try_into().unwrap()),
254 )?;
255
256 let Some(epfd) = this.machine.fds.get(epfd_value) else {
257 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
258 };
259 let Some(epfd) = epfd.downcast::<Epoll>() else {
260 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
261 };
262
263 if timeout == 0 || epfd.watcher.ready_count() != 0 {
264 this.return_ready_list(&epfd, dest, &event)?;
266 } else {
267 let deadline = match timeout {
269 0.. => {
270 let duration = Duration::from_millis(timeout.try_into().unwrap());
271 Some(this.machine.monotonic_clock.now().add_lossy(duration).into())
272 }
273 -1 => None,
274 ..-1 => {
275 throw_unsup_format!(
276 "epoll_wait: Only timeout values greater than or equal to -1 are supported."
277 );
278 }
279 };
280
281 epfd.watcher.add_blocked_thread(this.active_thread());
283 let dest = dest.clone();
285 this.block_thread(
289 BlockReason::Readiness,
290 deadline,
291 callback!(
292 @capture<'tcx> {
293 epfd: FileDescriptionRef<Epoll>,
294 dest: MPlaceTy<'tcx>,
295 event: MPlaceTy<'tcx>,
296 }
297 |this, unblock: UnblockKind| {
298 match unblock {
299 UnblockKind::Ready => {
300 let events = this.return_ready_list(&epfd, &dest, &event)?;
301 assert!(events > 0, "we got woken up with no events to deliver");
302 interp_ok(())
303 },
304 UnblockKind::TimedOut => {
305 epfd.watcher.remove_blocked_thread(this.active_thread());
307 this.write_int(0, &dest)?;
308 interp_ok(())
309 },
310 }
311 }
312 ),
313 );
314 }
315 interp_ok(())
316 }
317}
318
319impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
320trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
321 fn readiness_to_epoll_bitflag(&self, readiness: &Readiness) -> u32 {
324 let this = self.eval_context_ref();
325
326 let epollin = this.eval_libc_u32("EPOLLIN");
327 let epollout = this.eval_libc_u32("EPOLLOUT");
328 let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
329 let epollhup = this.eval_libc_u32("EPOLLHUP");
330 let epollerr = this.eval_libc_u32("EPOLLERR");
331
332 let mut bitflag = 0;
333 if readiness.readable {
334 bitflag |= epollin;
335 }
336 if readiness.writable {
337 bitflag |= epollout;
338 }
339 if readiness.read_closed {
340 bitflag |= epollrdhup;
341 }
342 if readiness.write_closed {
343 bitflag |= epollhup;
344 }
345 if readiness.error {
346 bitflag |= epollerr;
347 }
348 bitflag
349 }
350
351 fn epoll_bitflag_to_readiness(&self, bitflag: u32) -> Readiness {
354 let this = self.eval_context_ref();
355
356 let epollin = this.eval_libc_u32("EPOLLIN");
357 let epollout = this.eval_libc_u32("EPOLLOUT");
358 let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
359 let epollhup = this.eval_libc_u32("EPOLLHUP");
360 let epollerr = this.eval_libc_u32("EPOLLERR");
361
362 Readiness {
363 readable: bitflag & epollin == epollin,
364 writable: bitflag & epollout == epollout,
365 read_closed: bitflag & epollrdhup == epollrdhup,
366 write_closed: bitflag & epollhup == epollhup,
367 error: bitflag & epollerr == epollerr,
368 }
369 }
370
371 fn return_ready_list(
374 &mut self,
375 epfd: &FileDescriptionRef<Epoll>,
376 dest: &MPlaceTy<'tcx>,
377 events: &MPlaceTy<'tcx>,
378 ) -> InterpResult<'tcx, i32> {
379 let this = self.eval_context_mut();
380
381 let mut num_of_events = 0i32;
382 let mut array_iter = this.project_array_fields(events)?;
383 let max_events_num: usize = events.len(this)?.try_into().unwrap();
384
385 for interest in epfd.watcher.get_ready_interests(max_events_num, this)? {
388 let (_idx, slot) = array_iter.next(this)?.expect("Array should have slot for interest");
389 this.write_int_fields_named(
391 &[
392 ("events", this.readiness_to_epoll_bitflag(interest.active()).into()),
393 ("u64", interest.data.into()),
394 ],
395 &slot,
396 )?;
397 num_of_events = num_of_events.strict_add(1);
398 }
399 this.write_int(num_of_events, dest)?;
400 interp_ok(num_of_events)
401 }
402}