shadow_rs/host/descriptor/
eventfd.rs1use std::io::{Read, Write};
2
3use linux_api::errno::Errno;
4use linux_api::ioctls::IoctlRequest;
5use shadow_shim_helper_rs::syscall_types::ForeignPtr;
6
7use crate::cshadow as c;
8use crate::host::descriptor::listener::{StateEventSource, StateListenHandle, StateListenerFilter};
9use crate::host::descriptor::{FileMode, FileSignals, FileState, FileStatus};
10use crate::host::memory_manager::MemoryManager;
11use crate::host::syscall::io::{IoVec, IoVecReader, IoVecWriter};
12use crate::host::syscall::types::{SyscallError, SyscallResult};
13use crate::utility::HostTreePointer;
14use crate::utility::callback_queue::CallbackQueue;
15
16pub struct EventFd {
17 counter: u64,
18 is_semaphore_mode: bool,
19 event_source: StateEventSource,
20 state: FileState,
21 status: FileStatus,
22 has_open_file: bool,
25}
26
27impl EventFd {
28 pub fn new(init_value: u64, is_semaphore_mode: bool, status: FileStatus) -> Self {
29 Self {
30 counter: init_value,
31 is_semaphore_mode,
32 event_source: StateEventSource::new(),
33 state: FileState::ACTIVE | FileState::WRITABLE,
34 status,
35 has_open_file: false,
36 }
37 }
38
39 pub fn status(&self) -> FileStatus {
40 self.status
41 }
42
43 pub fn set_status(&mut self, status: FileStatus) {
44 self.status = status;
45 }
46
47 pub fn mode(&self) -> FileMode {
48 FileMode::READ | FileMode::WRITE
49 }
50
51 pub fn has_open_file(&self) -> bool {
52 self.has_open_file
53 }
54
55 pub fn supports_sa_restart(&self) -> bool {
56 false
57 }
58
59 pub fn set_has_open_file(&mut self, val: bool) {
60 self.has_open_file = val;
61 }
62
63 pub fn close(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
64 self.update_state(
66 FileState::CLOSED | FileState::ACTIVE | FileState::READABLE | FileState::WRITABLE,
67 FileState::CLOSED,
68 FileSignals::empty(),
69 cb_queue,
70 );
71
72 Ok(())
73 }
74
75 pub fn readv(
76 &mut self,
77 iovs: &[IoVec],
78 offset: Option<libc::off_t>,
79 _flags: libc::c_int,
80 mem: &mut MemoryManager,
81 cb_queue: &mut CallbackQueue,
82 ) -> Result<libc::ssize_t, SyscallError> {
83 if offset.is_some() {
85 return Err(Errno::ESPIPE.into());
86 }
87
88 const NUM_BYTES: usize = 8;
90
91 let len: libc::size_t = iovs.iter().map(|x| x.len).sum();
92
93 if len < NUM_BYTES {
96 log::trace!("Reading from eventfd requires a buffer of at least {NUM_BYTES} bytes");
97 return Err(Errno::EINVAL.into());
98 }
99
100 if self.counter == 0 {
101 log::trace!("Eventfd counter is 0 and cannot be read right now");
102 return Err(Errno::EWOULDBLOCK.into());
103 }
104
105 let mut writer = IoVecWriter::new(iovs, mem);
106
107 if self.is_semaphore_mode {
109 const ONE: [u8; NUM_BYTES] = 1u64.to_ne_bytes();
110 writer.write_all(&ONE)?;
111 self.counter -= 1;
112 } else {
113 let to_write: [u8; NUM_BYTES] = self.counter.to_ne_bytes();
114 writer.write_all(&to_write)?;
115 self.counter = 0;
116 }
117
118 self.refresh_state(FileSignals::empty(), cb_queue);
119
120 Ok(NUM_BYTES.try_into().unwrap())
121 }
122
123 pub fn writev(
124 &mut self,
125 iovs: &[IoVec],
126 offset: Option<libc::off_t>,
127 _flags: libc::c_int,
128 mem: &mut MemoryManager,
129 cb_queue: &mut CallbackQueue,
130 ) -> Result<libc::ssize_t, SyscallError> {
131 if offset.is_some() {
133 return Err(Errno::ESPIPE.into());
134 }
135
136 const NUM_BYTES: usize = 8;
139
140 let len: libc::size_t = iovs.iter().map(|x| x.len).sum();
141
142 if len < NUM_BYTES {
145 log::trace!("Writing to eventfd requires a buffer with at least {NUM_BYTES} bytes");
146 return Err(Errno::EINVAL.into());
147 }
148
149 if iovs.len() > 1 {
150 return Err(Errno::EINVAL.into());
152 }
153
154 let mut reader = IoVecReader::new(iovs, mem);
155
156 let mut read_buf = [0u8; NUM_BYTES];
157 reader.read_exact(&mut read_buf)?;
158 let value: u64 = u64::from_ne_bytes(read_buf);
159
160 if value == u64::MAX {
161 log::trace!("We do not allow writing the max counter value");
162 return Err(Errno::EINVAL.into());
163 }
164
165 const MAX_ALLOWED: u64 = u64::MAX - 1;
166 if value > MAX_ALLOWED - self.counter {
167 log::trace!("The write value does not currently fit into the counter");
168 return Err(Errno::EWOULDBLOCK.into());
169 }
170
171 self.counter += value;
172 let signals = if self.counter > 0 {
173 FileSignals::READ_BUFFER_GREW
174 } else {
175 FileSignals::empty()
176 };
177 self.refresh_state(signals, cb_queue);
178
179 Ok(NUM_BYTES.try_into().unwrap())
180 }
181
182 pub fn ioctl(
183 &mut self,
184 request: IoctlRequest,
185 _arg_ptr: ForeignPtr<()>,
186 _memory_manager: &mut MemoryManager,
187 ) -> SyscallResult {
188 log::warn!("We do not yet handle ioctl request {request:?} on eventfds");
189 Err(Errno::EINVAL.into())
190 }
191
192 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
193 warn_once_then_debug!("We do not yet handle stat calls on eventfds");
194 Err(Errno::EINVAL.into())
195 }
196
197 pub fn lseek(
198 &mut self,
199 _off: linux_api::posix_types::kernel_off_t,
200 _whence: linux_api::unistd::LSeekWhence,
201 ) -> Result<linux_api::posix_types::kernel_off_t, SyscallError> {
202 warn_once_then_debug!("We do not yet handle lseek calls on eventfds");
203 Err(Errno::EBADF.into())
204 }
205
206 pub fn add_listener(
207 &mut self,
208 monitoring_state: FileState,
209 monitoring_signals: FileSignals,
210 filter: StateListenerFilter,
211 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
212 + Send
213 + Sync
214 + 'static,
215 ) -> StateListenHandle {
216 self.event_source
217 .add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
218 }
219
220 pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
221 self.event_source.add_legacy_listener(ptr);
222 }
223
224 pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
225 self.event_source.remove_legacy_listener(ptr);
226 }
227
228 pub fn state(&self) -> FileState {
229 self.state
230 }
231
232 fn refresh_state(&mut self, signals: FileSignals, cb_queue: &mut CallbackQueue) {
233 if self.state.contains(FileState::CLOSED) {
234 return;
235 }
236
237 let mut readable_writable = FileState::empty();
238
239 readable_writable.set(FileState::READABLE, self.counter > 0);
241 readable_writable.set(FileState::WRITABLE, self.counter < u64::MAX - 1);
243
244 self.update_state(
245 FileState::READABLE | FileState::WRITABLE,
246 readable_writable,
247 signals,
248 cb_queue,
249 );
250 }
251
252 fn update_state(
253 &mut self,
254 mask: FileState,
255 state: FileState,
256 signals: FileSignals,
257 cb_queue: &mut CallbackQueue,
258 ) {
259 let old_state = self.state;
260
261 self.state.remove(mask);
263 self.state.insert(state & mask);
264
265 self.handle_state_change(old_state, signals, cb_queue);
266 }
267
268 fn handle_state_change(
269 &mut self,
270 old_state: FileState,
271 signals: FileSignals,
272 cb_queue: &mut CallbackQueue,
273 ) {
274 let states_changed = self.state ^ old_state;
275
276 if states_changed.is_empty() && signals.is_empty() {
278 return;
279 }
280
281 self.event_source
282 .notify_listeners(self.state, states_changed, signals, cb_queue);
283 }
284}