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!("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        // behavior defined in `man 2 eventfd`
108        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        // eventfds don't support seeking
132        if offset.is_some() {
133            return Err(Errno::ESPIPE.into());
134        }
135
136        // eventfd(2): "A write(2) call adds the 8-byte integer value supplied in its buffer to the
137        // counter"
138        const NUM_BYTES: usize = 8;
139
140        let len: libc::size_t = iovs.iter().map(|x| x.len).sum();
141
142        // this check doesn't guarentee that we can read all bytes since the stream length is only
143        // a hint
144        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            // Linux doesn't seem to let you write to an eventfd with multiple iovecs
151            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 add_listener(
198        &mut self,
199        monitoring_state: FileState,
200        monitoring_signals: FileSignals,
201        filter: StateListenerFilter,
202        notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
203        + Send
204        + Sync
205        + 'static,
206    ) -> StateListenHandle {
207        self.event_source
208            .add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
209    }
210
211    pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
212        self.event_source.add_legacy_listener(ptr);
213    }
214
215    pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
216        self.event_source.remove_legacy_listener(ptr);
217    }
218
219    pub fn state(&self) -> FileState {
220        self.state
221    }
222
223    fn refresh_state(&mut self, signals: FileSignals, cb_queue: &mut CallbackQueue) {
224        if self.state.contains(FileState::CLOSED) {
225            return;
226        }
227
228        let mut readable_writable = FileState::empty();
229
230        // set the descriptor as readable if we have a non-zero counter
231        readable_writable.set(FileState::READABLE, self.counter > 0);
232        // set the descriptor as writable if we can write a value of at least 1
233        readable_writable.set(FileState::WRITABLE, self.counter < u64::MAX - 1);
234
235        self.update_state(
236            FileState::READABLE | FileState::WRITABLE,
237            readable_writable,
238            signals,
239            cb_queue,
240        );
241    }
242
243    fn update_state(
244        &mut self,
245        mask: FileState,
246        state: FileState,
247        signals: FileSignals,
248        cb_queue: &mut CallbackQueue,
249    ) {
250        let old_state = self.state;
251
252        // remove the masked flags, then copy the masked flags
253        self.state.remove(mask);
254        self.state.insert(state & mask);
255
256        self.handle_state_change(old_state, signals, cb_queue);
257    }
258
259    fn handle_state_change(
260        &mut self,
261        old_state: FileState,
262        signals: FileSignals,
263        cb_queue: &mut CallbackQueue,
264    ) {
265        let states_changed = self.state ^ old_state;
266
267        // if nothing changed
268        if states_changed.is_empty() && signals.is_empty() {
269            return;
270        }
271
272        self.event_source
273            .notify_listeners(self.state, states_changed, signals, cb_queue);
274    }
275}