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<'tcx>(
32 self: FileDescriptionRef<Self>,
33 _ecx: &MiriInterpCx<'tcx>,
34 ) -> FileDescriptionRef<dyn UnixFileDescription> {
35 self
36 }
37}
38
39impl UnixFileDescription for Epoll {}
40
41impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
42pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
43 fn epoll_create1(&mut self, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
49 let this = self.eval_context_mut();
50
51 let flags = this.read_scalar(flags)?.to_i32()?;
52
53 let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC");
54
55 if flags != epoll_cloexec && flags != 0 {
57 throw_unsup_format!(
58 "epoll_create1: flag {:#x} is unsupported, only 0 or EPOLL_CLOEXEC are allowed",
59 flags
60 );
61 }
62
63 let fd =
64 this.machine.fds.insert_new(Epoll { watcher: Rc::new(ReadinessWatcher::default()) });
65 interp_ok(Scalar::from_i32(fd))
66 }
67
68 fn epoll_ctl(
82 &mut self,
83 epfd: &OpTy<'tcx>,
84 op: &OpTy<'tcx>,
85 fd: &OpTy<'tcx>,
86 event: &OpTy<'tcx>,
87 ) -> InterpResult<'tcx, Scalar> {
88 let this = self.eval_context_mut();
89
90 let epfd_value = this.read_scalar(epfd)?.to_i32()?;
91 let op = this.read_scalar(op)?.to_i32()?;
92 let fd = this.read_scalar(fd)?.to_i32()?;
93 let event = this.deref_pointer_as(event, this.libc_ty_layout("epoll_event"))?;
94
95 let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD");
96 let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD");
97 let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL");
98 let epollin = this.eval_libc_u32("EPOLLIN");
99 let epollout = this.eval_libc_u32("EPOLLOUT");
100 let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
101 let epollet = this.eval_libc_u32("EPOLLET");
102 let epollhup = this.eval_libc_u32("EPOLLHUP");
103 let epollerr = this.eval_libc_u32("EPOLLERR");
104
105 if epfd_value == fd {
107 return this.set_errno_and_return_neg1_i32(LibcError("EFAULT"));
108 }
109
110 let Some(epfd) = this.machine.fds.get(epfd_value) else {
112 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
113 };
114 let epfd = epfd
115 .downcast::<Epoll>()
116 .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?;
117
118 let Some(fd_ref) = this.machine.fds.get(fd) else {
119 return this.set_errno_and_return_neg1_i32(LibcError("EBADF"));
120 };
121 let id = fd_ref.id();
122 let interest_key = (id, fd);
123
124 if op == epoll_ctl_add || op == epoll_ctl_mod {
125 let mut relevant_bitflag =
127 this.read_scalar(&this.project_field(&event, FieldIdx::ZERO)?)?.to_u32()?;
128 let data = this.read_scalar(&this.project_field(&event, FieldIdx::ONE)?)?.to_u64()?;
129
130 let is_edge_triggered = if relevant_bitflag & epollet == epollet {
131 relevant_bitflag &= !epollet;
132 true
133 } else {
134 false
135 };
136
137 let mut flags = relevant_bitflag;
139 relevant_bitflag |= epollhup;
143 relevant_bitflag |= epollerr;
144
145 if flags & epollin == epollin {
146 flags &= !epollin;
147 }
148 if flags & epollout == epollout {
149 flags &= !epollout;
150 }
151 if flags & epollrdhup == epollrdhup {
152 flags &= !epollrdhup;
153 }
154 if flags & epollhup == epollhup {
155 flags &= !epollhup;
156 }
157 if flags & epollerr == epollerr {
158 flags &= !epollerr;
159 }
160 if flags != 0 {
161 throw_unsup_format!(
162 "epoll_ctl: encountered unknown unsupported flags {:#x}",
163 flags
164 );
165 }
166
167 let relevant = this.epoll_bitflag_to_readiness(relevant_bitflag);
168
169 if op == epoll_ctl_add {
170 let result =
172 epfd.watcher.add_interest(fd, relevant, is_edge_triggered, data, this)?;
173 if result.is_err() {
174 return this.set_errno_and_return_neg1_i32(LibcError("EEXIST"));
176 }
177 } else {
178 let result = epfd.watcher.update_interest(interest_key, this, |interest| {
180 interest.is_edge_triggered = is_edge_triggered;
181 interest.relevant = relevant;
182 interest.data = data;
183 })?;
184 if result.is_none() {
185 return this.set_errno_and_return_neg1_i32(LibcError("ENOENT"));
187 }
188 }
189 } else if op == epoll_ctl_del {
190 if epfd.watcher.remove_interest(interest_key).is_none() {
191 return this.set_errno_and_return_neg1_i32(LibcError("ENOENT"));
193 };
194 } else {
195 throw_unsup_format!("unsupported epoll_ctl operation: {op}");
196 }
197
198 interp_ok(Scalar::from_i32(0))
199 }
200
201 fn epoll_wait(
234 &mut self,
235 epfd: &OpTy<'tcx>,
236 events_op: &OpTy<'tcx>,
237 maxevents: &OpTy<'tcx>,
238 timeout: &OpTy<'tcx>,
239 dest: &MPlaceTy<'tcx>,
240 ) -> InterpResult<'tcx> {
241 let this = self.eval_context_mut();
242
243 let epfd_value = this.read_scalar(epfd)?.to_i32()?;
244 let events = this.read_immediate(events_op)?;
245 let maxevents = this.read_scalar(maxevents)?.to_i32()?;
246 let timeout = this.read_scalar(timeout)?.to_i32()?;
247
248 if epfd_value <= 0 || maxevents <= 0 {
249 return this.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
250 }
251
252 let event = this.deref_pointer_as(
255 &events,
256 this.libc_array_ty_layout("epoll_event", maxevents.try_into().unwrap()),
257 )?;
258
259 let Some(epfd) = this.machine.fds.get(epfd_value) else {
260 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
261 };
262 let Some(epfd) = epfd.downcast::<Epoll>() else {
263 return this.set_errno_and_return_neg1(LibcError("EBADF"), dest);
264 };
265
266 if timeout == 0 || epfd.watcher.ready_count() != 0 {
267 this.return_ready_list(&epfd, dest, &event)?;
269 } else {
270 let deadline = match timeout {
272 0.. => {
273 let duration = Duration::from_millis(timeout.try_into().unwrap());
274 Some(this.machine.monotonic_clock.now().add_lossy(duration).into())
275 }
276 -1 => None,
277 ..-1 => {
278 throw_unsup_format!(
279 "epoll_wait: Only timeout values greater than or equal to -1 are supported."
280 );
281 }
282 };
283
284 epfd.watcher.add_blocked_thread(this.active_thread());
286 let dest = dest.clone();
288 this.block_thread(
292 BlockReason::Readiness,
293 deadline,
294 callback!(
295 @capture<'tcx> {
296 epfd: FileDescriptionRef<Epoll>,
297 dest: MPlaceTy<'tcx>,
298 event: MPlaceTy<'tcx>,
299 }
300 |this, unblock: UnblockKind| {
301 match unblock {
302 UnblockKind::Ready => {
303 let events = this.return_ready_list(&epfd, &dest, &event)?;
304 assert!(events > 0, "we got woken up with no events to deliver");
305 interp_ok(())
306 },
307 UnblockKind::TimedOut => {
308 epfd.watcher.remove_blocked_thread(this.active_thread());
310 this.write_int(0, &dest)?;
311 interp_ok(())
312 },
313 }
314 }
315 ),
316 );
317 }
318 interp_ok(())
319 }
320}
321
322impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
323trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
324 fn readiness_to_epoll_bitflag(&self, readiness: &Readiness) -> u32 {
327 let this = self.eval_context_ref();
328
329 let epollin = this.eval_libc_u32("EPOLLIN");
330 let epollout = this.eval_libc_u32("EPOLLOUT");
331 let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
332 let epollhup = this.eval_libc_u32("EPOLLHUP");
333 let epollerr = this.eval_libc_u32("EPOLLERR");
334
335 let mut bitflag = 0;
336 if readiness.readable {
337 bitflag |= epollin;
338 }
339 if readiness.writable {
340 bitflag |= epollout;
341 }
342 if readiness.read_closed {
343 bitflag |= epollrdhup;
344 }
345 if readiness.write_closed {
346 bitflag |= epollhup;
347 }
348 if readiness.error {
349 bitflag |= epollerr;
350 }
351 bitflag
352 }
353
354 fn epoll_bitflag_to_readiness(&self, bitflag: u32) -> Readiness {
357 let this = self.eval_context_ref();
358
359 let epollin = this.eval_libc_u32("EPOLLIN");
360 let epollout = this.eval_libc_u32("EPOLLOUT");
361 let epollrdhup = this.eval_libc_u32("EPOLLRDHUP");
362 let epollhup = this.eval_libc_u32("EPOLLHUP");
363 let epollerr = this.eval_libc_u32("EPOLLERR");
364
365 Readiness {
366 readable: bitflag & epollin == epollin,
367 writable: bitflag & epollout == epollout,
368 read_closed: bitflag & epollrdhup == epollrdhup,
369 write_closed: bitflag & epollhup == epollhup,
370 error: bitflag & epollerr == epollerr,
371 }
372 }
373
374 fn return_ready_list(
377 &mut self,
378 epfd: &FileDescriptionRef<Epoll>,
379 dest: &MPlaceTy<'tcx>,
380 events: &MPlaceTy<'tcx>,
381 ) -> InterpResult<'tcx, i32> {
382 let this = self.eval_context_mut();
383
384 let mut num_of_events = 0i32;
385 let mut array_iter = this.project_array_fields(events)?;
386 let max_events_num: usize = events.len(this)?.try_into().unwrap();
387
388 for interest in epfd.watcher.get_ready_interests(max_events_num, this)? {
391 let (_idx, slot) = array_iter.next(this)?.expect("Array should have slot for interest");
392 this.write_int_fields_named(
394 &[
395 ("events", this.readiness_to_epoll_bitflag(interest.active()).into()),
396 ("u64", interest.data.into()),
397 ],
398 &slot,
399 )?;
400 num_of_events = num_of_events.strict_add(1);
401 }
402 this.write_int(num_of_events, dest)?;
403 interp_ok(num_of_events)
404 }
405}