1use std::collections::BTreeMap;
2use std::rc::Rc;
3use std::time::Duration;
4
5use rustc_target::spec::Os;
6
7use crate::shims::files::{FD_LIMIT, FdNum};
8use crate::*;
9
10#[derive(Debug)]
13struct PollInterest<'tcx> {
14 revents_place: MPlaceTy<'tcx>,
16}
17
18impl VisitProvenance for PollInterest<'_> {
19 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
20 self.revents_place.visit_provenance(visit);
21 }
22}
23
24impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
25pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
26 fn poll(
27 &mut self,
28 fds: &OpTy<'tcx>,
29 nfds: &OpTy<'tcx>,
30 timeout: &OpTy<'tcx>,
31 dest: &MPlaceTy<'tcx>,
32 ) -> InterpResult<'tcx> {
33 let ecx = self.eval_context_mut();
34
35 let nfds: u64 = ecx.read_scalar(nfds)?.to_uint(nfds.layout.size)?.try_into().unwrap();
36 let timeout = ecx.read_scalar(timeout)?.to_i32()?;
37
38 if nfds > u64::try_from(FD_LIMIT).unwrap() {
40 return ecx.set_errno_and_return_neg1(LibcError("EINVAL"), dest);
41 }
42 let fds_arr = ecx.deref_pointer(fds)?;
43
44 let mut interests = BTreeMap::<FdNum, PollInterest<'tcx>>::new();
46 let mut invalid_interests = 0u32;
49
50 let watcher = Rc::new(ReadinessWatcher::default());
51
52 for idx in 0..nfds {
55 let pollfd = ecx.project_c_array_field(&fds_arr, idx)?;
56 let fd_field = ecx.project_field_named(&pollfd, "fd")?;
57 let fd_num = ecx.read_scalar(&fd_field)?.to_i32()?;
58 let events_field = ecx.project_field_named(&pollfd, "events")?;
59 let events = ecx.read_scalar(&events_field)?.to_u16()?;
60 let revents_field = ecx.project_field_named(&pollfd, "revents")?;
61
62 let relevant_events = ecx.poll_bitflag_to_readiness(events)?;
63
64 let revents = if ecx.machine.fds.get(fd_num).is_some() {
65 let interest = PollInterest { revents_place: revents_field.clone() };
67 if interests.try_insert(fd_num, interest).is_err() {
68 throw_unsup_format!(
69 "poll: providing multiple interests for the same file descriptor is unsupported"
70 )
71 }
72 watcher
73 .add_interest(
74 fd_num,
75 relevant_events,
76 false,
77 u64::try_from(fd_num).unwrap(),
78 ecx,
79 )?
80 .unwrap();
82
83 0
86 } else if fd_num.is_negative() {
87 0
90 } else {
91 invalid_interests = invalid_interests.strict_add(1);
97
98 ecx.eval_libc_u16("POLLNVAL")
99 };
100
101 ecx.write_scalar(Scalar::from_u16(revents), &revents_field)?;
102 }
103
104 if timeout == 0 || invalid_interests > 0 || watcher.ready_count() > 0 {
105 let count = ecx.write_ready_events(watcher, interests)?;
109 let total = count.strict_add(invalid_interests);
111
112 return ecx.write_scalar(Scalar::from_u32(total), dest);
113 }
114
115 watcher.add_blocked_thread(ecx.machine.threads.active_thread());
118
119 let deadline = if timeout.is_positive() {
120 let timeout_duration = Duration::from_millis(u64::try_from(timeout).unwrap());
121 Some(ecx.machine.monotonic_clock.now().add_lossy(timeout_duration).into())
122 } else {
123 None
125 };
126
127 let dest = dest.clone();
128 ecx.block_thread(
129 BlockReason::Readiness,
130 deadline,
131 callback!(
132 @capture<'tcx> {
133 watcher: Rc<ReadinessWatcher>,
134 interests: BTreeMap<FdNum, PollInterest<'tcx>>,
135 dest: MPlaceTy<'tcx>,
136 } |ecx, reason: UnblockKind| {
137 if let UnblockKind::TimedOut = reason {
138 return ecx.write_scalar(Scalar::from_u32(0), &dest);
139 }
140
141 let count = ecx.write_ready_events(watcher, interests)?;
142 ecx.write_scalar(Scalar::from_u32(count), &dest)
143 }
144 ),
145 );
146
147 interp_ok(())
148 }
149}
150
151impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
152trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
153 fn write_ready_events(
156 &mut self,
157 watcher: Rc<ReadinessWatcher>,
158 interests: BTreeMap<FdNum, PollInterest<'tcx>>,
159 ) -> InterpResult<'tcx, u32> {
160 let this = self.eval_context_mut();
161
162 let mut fulfilled_interests = 0u32;
164
165 for ready in watcher.get_ready_interests(watcher.ready_count(), this)? {
168 let fd_num = FdNum::try_from(ready.data).expect("Data is always a file descriptor");
169 let interest = interests.get(&fd_num).expect("Interest should exist");
170
171 fulfilled_interests = fulfilled_interests.strict_add(1);
172
173 let poll_events = this.readiness_to_poll_bitflag(ready.active());
174 this.write_scalar(Scalar::from_u16(poll_events), &interest.revents_place)?;
175 }
176
177 interp_ok(fulfilled_interests)
178 }
179
180 fn readiness_to_poll_bitflag(&self, readiness: &Readiness) -> u16 {
183 let this = self.eval_context_ref();
184
185 let pollin = this.eval_libc_u16("POLLIN");
186 let pollout = this.eval_libc_u16("POLLOUT");
187 let pollhup = this.eval_libc_u16("POLLHUP");
188 let pollerr = this.eval_libc_u16("POLLERR");
189
190 let mut bitflag = 0;
191 if readiness.readable {
192 bitflag |= pollin;
193 }
194 if readiness.writable {
195 bitflag |= pollout;
196 }
197 if readiness.write_closed {
198 bitflag |= pollhup;
199 }
200 if readiness.error {
201 bitflag |= pollerr;
202 }
203
204 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
205 let pollrdhup = this.eval_libc_u16("POLLRDHUP");
207 if readiness.read_closed {
208 bitflag |= pollrdhup;
209 }
210 }
211
212 bitflag
213 }
214
215 fn poll_bitflag_to_readiness(&self, mut bitflag: u16) -> InterpResult<'tcx, Readiness> {
220 let this = self.eval_context_ref();
221
222 let pollin = this.eval_libc_u16("POLLIN");
223 let pollout = this.eval_libc_u16("POLLOUT");
224 let pollhup = this.eval_libc_u16("POLLHUP");
225 let pollerr = this.eval_libc_u16("POLLERR");
226 let pollnval = this.eval_libc_u16("POLLNVAL");
227
228 let mut readiness = Readiness { write_closed: true, error: true, ..Readiness::EMPTY };
230
231 if bitflag & pollin == pollin {
232 readiness.readable = true;
233 bitflag &= !pollin;
234 }
235 if bitflag & pollout == pollout {
236 readiness.writable = true;
237 bitflag &= !pollout;
238 }
239 if bitflag & pollhup == pollhup {
240 bitflag &= !pollhup;
241 }
242 if bitflag & pollerr == pollerr {
243 bitflag &= !pollerr;
244 }
245 if bitflag & pollnval == pollnval {
246 bitflag &= !pollnval;
248 }
249
250 if matches!(this.tcx.sess.target.os, Os::Linux | Os::Android | Os::FreeBsd | Os::Illumos) {
251 let pollrdhup = this.eval_libc_u16("POLLRDHUP");
253 if bitflag & pollrdhup == pollrdhup {
254 readiness.read_closed = true;
255 bitflag &= !pollrdhup;
256 }
257 }
258
259 if bitflag != 0 {
260 throw_unsup_format!(
261 "poll: poll event {bitflag:#x} is unsupported. Only POLLIN, \
262 POLLOUT, POLLERR, POLLHUP, POLLNVAL and POLLRDHUP are supported."
263 );
264 }
265
266 interp_ok(readiness)
267 }
268}