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