Skip to content

Commit 205ab4f

Browse files
committed
Auto merge of #2357 - DebugSteven:epoll_create1-shim, r=oli-obk
implement minimal epoll_create1 shim Implements minimal shim for #602
2 parents 1fca5a9 + 0dfa31b commit 205ab4f

File tree

12 files changed

+377
-41
lines changed

12 files changed

+377
-41
lines changed

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
clippy::single_element_loop,
2828
clippy::needless_return,
2929
clippy::bool_to_int_with_if,
30+
clippy::box_default,
3031
// We are not implementing queries here so it's fine
3132
rustc::potential_query_instability
3233
)]

src/machine.rs

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ use crate::{
3131
*,
3232
};
3333

34+
/// The number of the available real-time signal with the lowest priority.
35+
/// Dummy constant related to epoll, must be between 32 and 64.
36+
pub const SIGRTMAX: i32 = 42;
37+
3438
/// Extra data stored with each stack frame
3539
pub struct FrameExtra<'tcx> {
3640
/// Extra data for Stacked Borrows.

src/shims/unix/fs.rs

+20-15
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,25 @@ use crate::shims::os_str::bytes_to_os_str;
1717
use crate::*;
1818
use shims::os_str::os_str_to_bytes;
1919
use shims::time::system_time_to_duration;
20+
use shims::unix::linux::fd::epoll::Epoll;
2021

2122
#[derive(Debug)]
22-
struct FileHandle {
23+
pub struct FileHandle {
2324
file: File,
2425
writable: bool,
2526
}
2627

27-
trait FileDescriptor: std::fmt::Debug {
28+
pub trait FileDescriptor: std::fmt::Debug {
2829
fn name(&self) -> &'static str;
2930

3031
fn as_file_handle<'tcx>(&self) -> InterpResult<'tcx, &FileHandle> {
3132
throw_unsup_format!("{} cannot be used as FileHandle", self.name());
3233
}
3334

35+
fn as_epoll_handle<'tcx>(&mut self) -> InterpResult<'tcx, &mut Epoll> {
36+
throw_unsup_format!("not an epoll file descriptor");
37+
}
38+
3439
fn read<'tcx>(
3540
&mut self,
3641
_communicate_allowed: bool,
@@ -274,7 +279,7 @@ impl FileDescriptor for NullOutput {
274279

275280
#[derive(Debug)]
276281
pub struct FileHandler {
277-
handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
282+
pub handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
278283
}
279284

280285
impl VisitTags for FileHandler {
@@ -297,7 +302,7 @@ impl FileHandler {
297302
FileHandler { handles }
298303
}
299304

300-
fn insert_fd(&mut self, file_handle: Box<dyn FileDescriptor>) -> i32 {
305+
pub fn insert_fd(&mut self, file_handle: Box<dyn FileDescriptor>) -> i32 {
301306
self.insert_fd_with_min_fd(file_handle, 0)
302307
}
303308

@@ -376,17 +381,6 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx
376381
Ok(0)
377382
}
378383

379-
/// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
380-
/// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
381-
/// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
382-
/// types (like `read`, that returns an `i64`).
383-
fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
384-
let this = self.eval_context_mut();
385-
let ebadf = this.eval_libc("EBADF");
386-
this.set_last_error(ebadf)?;
387-
Ok((-1).into())
388-
}
389-
390384
fn file_type_to_d_type(
391385
&mut self,
392386
file_type: std::io::Result<FileType>,
@@ -726,6 +720,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
726720
))
727721
}
728722

723+
/// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
724+
/// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
725+
/// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
726+
/// types (like `read`, that returns an `i64`).
727+
fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
728+
let this = self.eval_context_mut();
729+
let ebadf = this.eval_libc("EBADF");
730+
this.set_last_error(ebadf)?;
731+
Ok((-1).into())
732+
}
733+
729734
fn read(
730735
&mut self,
731736
fd: i32,

src/shims/unix/linux/fd.rs

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
use rustc_middle::ty::ScalarInt;
2+
3+
use crate::*;
4+
use epoll::{Epoll, EpollEvent};
5+
use event::Event;
6+
use socketpair::SocketPair;
7+
8+
use shims::unix::fs::EvalContextExt as _;
9+
10+
pub mod epoll;
11+
pub mod event;
12+
pub mod socketpair;
13+
14+
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
15+
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
16+
/// This function returns a file descriptor referring to the new `Epoll` instance. This file
17+
/// descriptor is used for all subsequent calls to the epoll interface. If the `flags` argument
18+
/// is 0, then this function is the same as `epoll_create()`.
19+
///
20+
/// <https://linux.die.net/man/2/epoll_create1>
21+
fn epoll_create1(
22+
&mut self,
23+
flags: &OpTy<'tcx, Provenance>,
24+
) -> InterpResult<'tcx, Scalar<Provenance>> {
25+
let this = self.eval_context_mut();
26+
27+
let flags = this.read_scalar(flags)?.to_i32()?;
28+
29+
let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC");
30+
if flags == epoll_cloexec {
31+
// Miri does not support exec, so this flag has no effect.
32+
} else if flags != 0 {
33+
throw_unsup_format!("epoll_create1 flags {flags} are not implemented");
34+
}
35+
36+
let fd = this.machine.file_handler.insert_fd(Box::new(Epoll::default()));
37+
Ok(Scalar::from_i32(fd))
38+
}
39+
40+
/// This function performs control operations on the `Epoll` instance referred to by the file
41+
/// descriptor `epfd`. It requests that the operation `op` be performed for the target file
42+
/// descriptor, `fd`.
43+
///
44+
/// Valid values for the op argument are:
45+
/// `EPOLL_CTL_ADD` - Register the target file descriptor `fd` on the `Epoll` instance referred
46+
/// to by the file descriptor `epfd` and associate the event `event` with the internal file
47+
/// linked to `fd`.
48+
/// `EPOLL_CTL_MOD` - Change the event `event` associated with the target file descriptor `fd`.
49+
/// `EPOLL_CTL_DEL` - Deregister the target file descriptor `fd` from the `Epoll` instance
50+
/// referred to by `epfd`. The `event` is ignored and can be null.
51+
///
52+
/// <https://linux.die.net/man/2/epoll_ctl>
53+
fn epoll_ctl(
54+
&mut self,
55+
epfd: &OpTy<'tcx, Provenance>,
56+
op: &OpTy<'tcx, Provenance>,
57+
fd: &OpTy<'tcx, Provenance>,
58+
event: &OpTy<'tcx, Provenance>,
59+
) -> InterpResult<'tcx, Scalar<Provenance>> {
60+
let this = self.eval_context_mut();
61+
62+
let epfd = this.read_scalar(epfd)?.to_i32()?;
63+
let op = this.read_scalar(op)?.to_i32()?;
64+
let fd = this.read_scalar(fd)?.to_i32()?;
65+
let _event = this.read_scalar(event)?.to_pointer(this)?;
66+
67+
let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD");
68+
let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD");
69+
let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL");
70+
71+
if op == epoll_ctl_add || op == epoll_ctl_mod {
72+
let event = this.deref_operand(event)?;
73+
74+
let events = this.mplace_field(&event, 0)?;
75+
let events = this.read_scalar(&events.into())?.to_u32()?;
76+
let data = this.mplace_field(&event, 1)?;
77+
let data = this.read_scalar(&data.into())?;
78+
let event = EpollEvent { events, data };
79+
80+
if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
81+
let epfd = epfd.as_epoll_handle()?;
82+
83+
epfd.file_descriptors.insert(fd, event);
84+
Ok(Scalar::from_i32(0))
85+
} else {
86+
Ok(Scalar::from_i32(this.handle_not_found()?))
87+
}
88+
} else if op == epoll_ctl_del {
89+
if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
90+
let epfd = epfd.as_epoll_handle()?;
91+
92+
epfd.file_descriptors.remove(&fd);
93+
Ok(Scalar::from_i32(0))
94+
} else {
95+
Ok(Scalar::from_i32(this.handle_not_found()?))
96+
}
97+
} else {
98+
let einval = this.eval_libc("EINVAL");
99+
this.set_last_error(einval)?;
100+
Ok(Scalar::from_i32(-1))
101+
}
102+
}
103+
104+
/// This function creates an `Event` that is used as an event wait/notify mechanism by
105+
/// user-space applications, and by the kernel to notify user-space applications of events.
106+
/// The `Event` contains an `u64` counter maintained by the kernel. The counter is initialized
107+
/// with the value specified in the `initval` argument.
108+
///
109+
/// A new file descriptor referring to the `Event` is returned. The `read`, `write`, `poll`,
110+
/// `select`, and `close` operations can be performed on the file descriptor. For more
111+
/// information on these operations, see the man page linked below.
112+
///
113+
/// The `flags` are not currently implemented for eventfd.
114+
/// The `flags` may be bitwise ORed to change the behavior of `eventfd`:
115+
/// `EFD_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor.
116+
/// `EFD_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the new open file description.
117+
/// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics.
118+
///
119+
/// <https://linux.die.net/man/2/eventfd>
120+
fn eventfd(
121+
&mut self,
122+
val: &OpTy<'tcx, Provenance>,
123+
flags: &OpTy<'tcx, Provenance>,
124+
) -> InterpResult<'tcx, Scalar<Provenance>> {
125+
let this = self.eval_context_mut();
126+
127+
let val = this.read_scalar(val)?.to_u32()?;
128+
let flags = this.read_scalar(flags)?.to_i32()?;
129+
130+
let efd_cloexec = this.eval_libc_i32("EFD_CLOEXEC");
131+
let efd_nonblock = this.eval_libc_i32("EFD_NONBLOCK");
132+
let efd_semaphore = this.eval_libc_i32("EFD_SEMAPHORE");
133+
134+
if flags & (efd_cloexec | efd_nonblock | efd_semaphore) == 0 {
135+
throw_unsup_format!("{flags} is unsupported");
136+
}
137+
// FIXME handle the cloexec and nonblock flags
138+
if flags & efd_cloexec == efd_cloexec {}
139+
if flags & efd_nonblock == efd_nonblock {}
140+
if flags & efd_semaphore == efd_semaphore {
141+
throw_unsup_format!("EFD_SEMAPHORE is unsupported");
142+
}
143+
144+
let fh = &mut this.machine.file_handler;
145+
let fd = fh.insert_fd(Box::new(Event { val }));
146+
Ok(Scalar::from_i32(fd))
147+
}
148+
149+
/// Currently this function creates new `SocketPair`s without specifying the domain, type, or
150+
/// protocol of the new socket and these are stored in the socket values `sv` argument.
151+
///
152+
/// This function creates an unnamed pair of connected sockets in the specified domain, of the
153+
/// specified type, and using the optionally specified protocol.
154+
///
155+
/// The `domain` argument specified a communication domain; this selects the protocol family
156+
/// used for communication. The socket `type` specifies the communication semantics.
157+
/// The `protocol` specifies a particular protocol to use with the socket. Normally there's
158+
/// only a single protocol supported for a particular socket type within a given protocol
159+
/// family, in which case `protocol` can be specified as 0. It is possible that many protocols
160+
/// exist and in that case, a particular protocol must be specified.
161+
///
162+
/// For more information on the arguments see the socket manpage:
163+
/// <https://linux.die.net/man/2/socket>
164+
///
165+
/// <https://linux.die.net/man/2/socketpair>
166+
fn socketpair(
167+
&mut self,
168+
domain: &OpTy<'tcx, Provenance>,
169+
type_: &OpTy<'tcx, Provenance>,
170+
protocol: &OpTy<'tcx, Provenance>,
171+
sv: &OpTy<'tcx, Provenance>,
172+
) -> InterpResult<'tcx, Scalar<Provenance>> {
173+
let this = self.eval_context_mut();
174+
175+
let _domain = this.read_scalar(domain)?.to_i32()?;
176+
let _type_ = this.read_scalar(type_)?.to_i32()?;
177+
let _protocol = this.read_scalar(protocol)?.to_i32()?;
178+
let sv = this.deref_operand(sv)?;
179+
180+
let fh = &mut this.machine.file_handler;
181+
let sv0 = fh.insert_fd(Box::new(SocketPair));
182+
let sv0 = ScalarInt::try_from_int(sv0, sv.layout.size).unwrap();
183+
let sv1 = fh.insert_fd(Box::new(SocketPair));
184+
let sv1 = ScalarInt::try_from_int(sv1, sv.layout.size).unwrap();
185+
186+
this.write_scalar(sv0, &sv.into())?;
187+
this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?.into())?;
188+
189+
Ok(Scalar::from_i32(0))
190+
}
191+
}

src/shims/unix/linux/fd/epoll.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use crate::*;
2+
3+
use crate::shims::unix::fs::FileDescriptor;
4+
5+
use rustc_data_structures::fx::FxHashMap;
6+
use std::io;
7+
8+
/// An `Epoll` file descriptor connects file handles and epoll events
9+
#[derive(Clone, Debug, Default)]
10+
pub struct Epoll {
11+
/// The file descriptors we are watching, and what we are watching for.
12+
pub file_descriptors: FxHashMap<i32, EpollEvent>,
13+
}
14+
15+
/// Epoll Events associate events with data.
16+
/// These fields are currently unused by miri.
17+
/// This matches the `epoll_event` struct defined
18+
/// by the epoll_ctl man page. For more information
19+
/// see the man page:
20+
///
21+
/// <https://man7.org/linux/man-pages/man2/epoll_ctl.2.html>
22+
#[derive(Clone, Debug)]
23+
pub struct EpollEvent {
24+
pub events: u32,
25+
/// `Scalar<Provenance>` is used to represent the
26+
/// `epoll_data` type union.
27+
pub data: Scalar<Provenance>,
28+
}
29+
30+
impl FileDescriptor for Epoll {
31+
fn name(&self) -> &'static str {
32+
"epoll"
33+
}
34+
35+
fn as_epoll_handle<'tcx>(&mut self) -> InterpResult<'tcx, &mut Epoll> {
36+
Ok(self)
37+
}
38+
39+
fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
40+
Ok(Box::new(self.clone()))
41+
}
42+
43+
fn is_tty(&self) -> bool {
44+
false
45+
}
46+
47+
fn close<'tcx>(
48+
self: Box<Self>,
49+
_communicate_allowed: bool,
50+
) -> InterpResult<'tcx, io::Result<i32>> {
51+
Ok(Ok(0))
52+
}
53+
}

src/shims/unix/linux/fd/event.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use crate::shims::unix::fs::FileDescriptor;
2+
3+
use rustc_const_eval::interpret::InterpResult;
4+
5+
use std::io;
6+
7+
/// A kind of file descriptor created by `eventfd`.
8+
/// The `Event` type isn't currently written to by `eventfd`.
9+
/// The interface is meant to keep track of objects associated
10+
/// with a file descriptor. For more information see the man
11+
/// page below:
12+
///
13+
/// <https://man.netbsd.org/eventfd.2>
14+
#[derive(Debug)]
15+
pub struct Event {
16+
pub val: u32,
17+
}
18+
19+
impl FileDescriptor for Event {
20+
fn name(&self) -> &'static str {
21+
"event"
22+
}
23+
24+
fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
25+
Ok(Box::new(Event { val: self.val }))
26+
}
27+
28+
fn is_tty(&self) -> bool {
29+
false
30+
}
31+
32+
fn close<'tcx>(
33+
self: Box<Self>,
34+
_communicate_allowed: bool,
35+
) -> InterpResult<'tcx, io::Result<i32>> {
36+
Ok(Ok(0))
37+
}
38+
}

0 commit comments

Comments
 (0)