shadow_rs/host/descriptor/
eventfd.rs

1use 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    // should only be used by `OpenFile` to make sure there is only ever one `OpenFile` instance for
23    // this file
24    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        // set the closed flag and remove the active, readable, and writable flags
65        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        // eventfds don't support seeking
84        if offset.is_some() {
85            return Err(Errno::ESPIPE.into());
86        }
87
88        // eventfd(2): "Each successful read(2) returns an 8-byte integer"
89        const NUM_BYTES: usize = 8;
90
91        let len: libc::size_t = iovs.iter().map(|x| x.len).sum();
92
93        // this check doesn't guarentee that we can write all bytes since the stream length is only
94        // a hint
95        if len < NUM_BYTES {
96            log::trace!(
97                "Reading from eventfd requires a buffer of at least {} bytes",
98                NUM_BYTES
99            );
100            return Err(Errno::EINVAL.into());
101        }
102
103        if self.counter == 0 {
104            log::trace!("Eventfd counter is 0 and cannot be read right now");
105            return Err(Errno::EWOULDBLOCK.into());
106        }
107
108        let mut writer = IoVecWriter::new(iovs, mem);
109
110        // behavior defined in `man 2 eventfd`
111        if self.is_semaphore_mode {
112            const ONE: [u8; NUM_BYTES] = 1u64.to_ne_bytes();
113            writer.write_all(&ONE)?;
114            self.counter -= 1;
115        } else {
116            let to_write: [u8; NUM_BYTES] = self.counter.to_ne_bytes();
117            writer.write_all(&to_write)?;
118            self.counter = 0;
119        }
120
121        self.refresh_state(FileSignals::empty(), cb_queue);
122
123        Ok(NUM_BYTES.try_into().unwrap())
124    }
125
126    pub fn writev(
127        &mut self,
128        iovs: &[IoVec],
129        offset: Option<libc::off_t>,
130        _flags: libc::c_int,
131        mem: &mut MemoryManager,
132        cb_queue: &mut CallbackQueue,
133    ) -> Result<libc::ssize_t, SyscallError> {
134        // eventfds don't support seeking
135        if offset.is_some() {
136            return Err(Errno::ESPIPE.into());
137        }
138
139        // eventfd(2): "A write(2) call adds the 8-byte integer value supplied in its buffer to the
140        // counter"
141        const NUM_BYTES: usize = 8;
142
143        let len: libc::size_t = iovs.iter().map(|x| x.len).sum();
144
145        // this check doesn't guarentee that we can read all bytes since the stream length is only
146        // a hint
147        if len < NUM_BYTES {
148            log::trace!(
149                "Writing to eventfd requires a buffer with at least {} bytes",
150                NUM_BYTES
151            );
152            return Err(Errno::EINVAL.into());
153        }
154
155        if iovs.len() > 1 {
156            // Linux doesn't seem to let you write to an eventfd with multiple iovecs
157            return Err(Errno::EINVAL.into());
158        }
159
160        let mut reader = IoVecReader::new(iovs, mem);
161
162        let mut read_buf = [0u8; NUM_BYTES];
163        reader.read_exact(&mut read_buf)?;
164        let value: u64 = u64::from_ne_bytes(read_buf);
165
166        if value == u64::MAX {
167            log::trace!("We do not allow writing the max counter value");
168            return Err(Errno::EINVAL.into());
169        }
170
171        const MAX_ALLOWED: u64 = u64::MAX - 1;
172        if value > MAX_ALLOWED - self.counter {
173            log::trace!("The write value does not currently fit into the counter");
174            return Err(Errno::EWOULDBLOCK.into());
175        }
176
177        self.counter += value;
178        let signals = if self.counter > 0 {
179            FileSignals::READ_BUFFER_GREW
180        } else {
181            FileSignals::empty()
182        };
183        self.refresh_state(signals, cb_queue);
184
185        Ok(NUM_BYTES.try_into().unwrap())
186    }
187
188    pub fn ioctl(
189        &mut self,
190        request: IoctlRequest,
191        _arg_ptr: ForeignPtr<()>,
192        _memory_manager: &mut MemoryManager,
193    ) -> SyscallResult {
194        log::warn!("We do not yet handle ioctl request {request:?} on eventfds");
195        Err(Errno::EINVAL.into())
196    }
197
198    pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
199        warn_once_then_debug!("We do not yet handle stat calls on eventfds");
200        Err(Errno::EINVAL.into())
201    }
202
203    pub fn add_listener(
204        &mut self,
205        monitoring_state: FileState,
206        monitoring_signals: FileSignals,
207        filter: StateListenerFilter,
208        notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
209        + Send
210        + Sync
211        + 'static,
212    ) -> StateListenHandle {
213        self.event_source
214            .add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
215    }
216
217    pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
218        self.event_source.add_legacy_listener(ptr);
219    }
220
221    pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
222        self.event_source.remove_legacy_listener(ptr);
223    }
224
225    pub fn state(&self) -> FileState {
226        self.state
227    }
228
229    fn refresh_state(&mut self, signals: FileSignals, cb_queue: &mut CallbackQueue) {
230        if self.state.contains(FileState::CLOSED) {
231            return;
232        }
233
234        let mut readable_writable = FileState::empty();
235
236        // set the descriptor as readable if we have a non-zero counter
237        readable_writable.set(FileState::READABLE, self.counter > 0);
238        // set the descriptor as writable if we can write a value of at least 1
239        readable_writable.set(FileState::WRITABLE, self.counter < u64::MAX - 1);
240
241        self.update_state(
242            FileState::READABLE | FileState::WRITABLE,
243            readable_writable,
244            signals,
245            cb_queue,
246        );
247    }
248
249    fn update_state(
250        &mut self,
251        mask: FileState,
252        state: FileState,
253        signals: FileSignals,
254        cb_queue: &mut CallbackQueue,
255    ) {
256        let old_state = self.state;
257
258        // remove the masked flags, then copy the masked flags
259        self.state.remove(mask);
260        self.state.insert(state & mask);
261
262        self.handle_state_change(old_state, signals, cb_queue);
263    }
264
265    fn handle_state_change(
266        &mut self,
267        old_state: FileState,
268        signals: FileSignals,
269        cb_queue: &mut CallbackQueue,
270    ) {
271        let states_changed = self.state ^ old_state;
272
273        // if nothing changed
274        if states_changed.is_empty() && signals.is_empty() {
275            return;
276        }
277
278        self.event_source
279            .notify_listeners(self.state, states_changed, signals, cb_queue);
280    }
281}