1+ //! This implements "anonymous" sockets, that do not correspond to anything on the host system and
2+ //! are entirely implemented inside Miri.
3+ //! We also use the same infrastructure to implement unnamed pipes.
4+
15use std:: cell:: { OnceCell , RefCell } ;
26use std:: collections:: VecDeque ;
37use std:: io;
@@ -13,12 +17,13 @@ use crate::{concurrency::VClock, *};
1317/// be configured in the real system.
1418const MAX_SOCKETPAIR_BUFFER_CAPACITY : usize = 212992 ;
1519
16- /// Pair of connected sockets.
20+ /// One end of a pair of connected unnamed sockets.
1721#[ derive( Debug ) ]
18- struct SocketPair {
19- /// The buffer we are reading from.
20- readbuf : RefCell < Buffer > ,
21- /// The `SocketPair` file descriptor that is our "peer", and that holds the buffer we are
22+ struct AnonSocket {
23+ /// The buffer we are reading from, or `None` if this is the writing end of a pipe.
24+ /// (In that case, the peer FD will be the reading end of that pipe.)
25+ readbuf : Option < RefCell < Buffer > > ,
26+ /// The `AnonSocket` file descriptor that is our "peer", and that holds the buffer we are
2227 /// writing to. This is a weak reference because the other side may be closed before us; all
2328 /// future writes will then trigger EPIPE.
2429 peer_fd : OnceCell < WeakFileDescriptionRef > ,
@@ -37,13 +42,13 @@ impl Buffer {
3742 }
3843}
3944
40- impl SocketPair {
45+ impl AnonSocket {
4146 fn peer_fd ( & self ) -> & WeakFileDescriptionRef {
4247 self . peer_fd . get ( ) . unwrap ( )
4348 }
4449}
4550
46- impl FileDescription for SocketPair {
51+ impl FileDescription for AnonSocket {
4752 fn name ( & self ) -> & ' static str {
4853 "socketpair"
4954 }
@@ -55,17 +60,25 @@ impl FileDescription for SocketPair {
5560 let mut epoll_ready_events = EpollReadyEvents :: new ( ) ;
5661
5762 // Check if it is readable.
58- let readbuf = self . readbuf . borrow ( ) ;
59- if !readbuf. buf . is_empty ( ) {
63+ if let Some ( readbuf) = & self . readbuf {
64+ if !readbuf. borrow ( ) . buf . is_empty ( ) {
65+ epoll_ready_events. epollin = true ;
66+ }
67+ } else {
68+ // Without a read buffer, reading never blocks, so we are always ready.
6069 epoll_ready_events. epollin = true ;
6170 }
6271
6372 // Check if is writable.
6473 if let Some ( peer_fd) = self . peer_fd ( ) . upgrade ( ) {
65- let writebuf = & peer_fd. downcast :: < SocketPair > ( ) . unwrap ( ) . readbuf . borrow ( ) ;
66- let data_size = writebuf. buf . len ( ) ;
67- let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY . strict_sub ( data_size) ;
68- if available_space != 0 {
74+ if let Some ( writebuf) = & peer_fd. downcast :: < AnonSocket > ( ) . unwrap ( ) . readbuf {
75+ let data_size = writebuf. borrow ( ) . buf . len ( ) ;
76+ let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY . strict_sub ( data_size) ;
77+ if available_space != 0 {
78+ epoll_ready_events. epollout = true ;
79+ }
80+ } else {
81+ // Without a write buffer, writing never blocks.
6982 epoll_ready_events. epollout = true ;
7083 }
7184 } else {
@@ -108,7 +121,12 @@ impl FileDescription for SocketPair {
108121 return Ok ( Ok ( 0 ) ) ;
109122 }
110123
111- let mut readbuf = self . readbuf . borrow_mut ( ) ;
124+ let Some ( readbuf) = & self . readbuf else {
125+ // FIXME: This should return EBADF, but there's no nice way to do that as there's no
126+ // corresponding ErrorKind variant.
127+ throw_unsup_format ! ( "reading from the write end of a pipe" ) ;
128+ } ;
129+ let mut readbuf = readbuf. borrow_mut ( ) ;
112130 if readbuf. buf . is_empty ( ) {
113131 if self . peer_fd ( ) . upgrade ( ) . is_none ( ) {
114132 // Socketpair with no peer and empty buffer.
@@ -176,7 +194,13 @@ impl FileDescription for SocketPair {
176194 // closed.
177195 return Ok ( Err ( Error :: from ( ErrorKind :: BrokenPipe ) ) ) ;
178196 } ;
179- let mut writebuf = peer_fd. downcast :: < SocketPair > ( ) . unwrap ( ) . readbuf . borrow_mut ( ) ;
197+
198+ let Some ( writebuf) = & peer_fd. downcast :: < AnonSocket > ( ) . unwrap ( ) . readbuf else {
199+ // FIXME: This should return EBADF, but there's no nice way to do that as there's no
200+ // corresponding ErrorKind variant.
201+ throw_unsup_format ! ( "writing to the reading end of a pipe" ) ;
202+ } ;
203+ let mut writebuf = writebuf. borrow_mut ( ) ;
180204 let data_size = writebuf. buf . len ( ) ;
181205 let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY . strict_sub ( data_size) ;
182206 if available_space == 0 {
@@ -227,12 +251,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227251
228252 let mut is_sock_nonblock = false ;
229253
230- // Parse and remove the type flags that we support. If type != 0 after removing,
231- // unsupported flags are used.
232- if type_ & this. eval_libc_i32 ( "SOCK_STREAM" ) == this. eval_libc_i32 ( "SOCK_STREAM" ) {
233- type_ &= !( this. eval_libc_i32 ( "SOCK_STREAM" ) ) ;
234- }
235-
254+ // Parse and remove the type flags that we support.
236255 // SOCK_NONBLOCK only exists on Linux.
237256 if this. tcx . sess . target . os == "linux" {
238257 if type_ & this. eval_libc_i32 ( "SOCK_NONBLOCK" ) == this. eval_libc_i32 ( "SOCK_NONBLOCK" ) {
@@ -253,7 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
253272 and AF_LOCAL are allowed",
254273 domain
255274 ) ;
256- } else if type_ != 0 {
275+ } else if type_ != this . eval_libc_i32 ( "SOCK_STREAM" ) {
257276 throw_unsup_format ! (
258277 "socketpair: type {:#x} is unsupported, only SOCK_STREAM, \
259278 SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
@@ -268,20 +287,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
268287
269288 // Generate file descriptions.
270289 let fds = & mut this. machine . fds ;
271- let fd0 = fds. new_ref ( SocketPair {
272- readbuf : RefCell :: new ( Buffer :: new ( ) ) ,
290+ let fd0 = fds. new_ref ( AnonSocket {
291+ readbuf : Some ( RefCell :: new ( Buffer :: new ( ) ) ) ,
273292 peer_fd : OnceCell :: new ( ) ,
274293 is_nonblock : is_sock_nonblock,
275294 } ) ;
276- let fd1 = fds. new_ref ( SocketPair {
277- readbuf : RefCell :: new ( Buffer :: new ( ) ) ,
295+ let fd1 = fds. new_ref ( AnonSocket {
296+ readbuf : Some ( RefCell :: new ( Buffer :: new ( ) ) ) ,
278297 peer_fd : OnceCell :: new ( ) ,
279298 is_nonblock : is_sock_nonblock,
280299 } ) ;
281300
282301 // Make the file descriptions point to each other.
283- fd0. downcast :: < SocketPair > ( ) . unwrap ( ) . peer_fd . set ( fd1. downgrade ( ) ) . unwrap ( ) ;
284- fd1. downcast :: < SocketPair > ( ) . unwrap ( ) . peer_fd . set ( fd0. downgrade ( ) ) . unwrap ( ) ;
302+ fd0. downcast :: < AnonSocket > ( ) . unwrap ( ) . peer_fd . set ( fd1. downgrade ( ) ) . unwrap ( ) ;
303+ fd1. downcast :: < AnonSocket > ( ) . unwrap ( ) . peer_fd . set ( fd0. downgrade ( ) ) . unwrap ( ) ;
285304
286305 // Insert the file description to the fd table, generating the file descriptors.
287306 let sv0 = fds. insert ( fd0) ;
@@ -295,4 +314,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
295314
296315 Ok ( Scalar :: from_i32 ( 0 ) )
297316 }
317+
318+ fn pipe2 (
319+ & mut self ,
320+ pipefd : & OpTy < ' tcx > ,
321+ flags : Option < & OpTy < ' tcx > > ,
322+ ) -> InterpResult < ' tcx , Scalar > {
323+ let this = self . eval_context_mut ( ) ;
324+
325+ let pipefd = this. deref_pointer ( pipefd) ?;
326+ let flags = match flags {
327+ Some ( flags) => this. read_scalar ( flags) ?. to_i32 ( ) ?,
328+ None => 0 ,
329+ } ;
330+
331+ // As usual we ignore CLOEXEC.
332+ let cloexec = this. eval_libc_i32 ( "O_CLOEXEC" ) ;
333+ if flags != 0 && flags != cloexec {
334+ throw_unsup_format ! ( "unsupported flags in `pipe2`" ) ;
335+ }
336+
337+ // Generate file descriptions.
338+ // pipefd[0] refers to the read end of the pipe.
339+ let fds = & mut this. machine . fds ;
340+ let fd0 = fds. new_ref ( AnonSocket {
341+ readbuf : Some ( RefCell :: new ( Buffer :: new ( ) ) ) ,
342+ peer_fd : OnceCell :: new ( ) ,
343+ is_nonblock : false ,
344+ } ) ;
345+ let fd1 =
346+ fds. new_ref ( AnonSocket { readbuf : None , peer_fd : OnceCell :: new ( ) , is_nonblock : false } ) ;
347+
348+ // Make the file descriptions point to each other.
349+ fd0. downcast :: < AnonSocket > ( ) . unwrap ( ) . peer_fd . set ( fd1. downgrade ( ) ) . unwrap ( ) ;
350+ fd1. downcast :: < AnonSocket > ( ) . unwrap ( ) . peer_fd . set ( fd0. downgrade ( ) ) . unwrap ( ) ;
351+
352+ // Insert the file description to the fd table, generating the file descriptors.
353+ let pipefd0 = fds. insert ( fd0) ;
354+ let pipefd1 = fds. insert ( fd1) ;
355+
356+ // Return file descriptors to the caller.
357+ let pipefd0 = Scalar :: from_int ( pipefd0, pipefd. layout . size ) ;
358+ let pipefd1 = Scalar :: from_int ( pipefd1, pipefd. layout . size ) ;
359+ this. write_scalar ( pipefd0, & pipefd) ?;
360+ this. write_scalar ( pipefd1, & pipefd. offset ( pipefd. layout . size , pipefd. layout , this) ?) ?;
361+
362+ Ok ( Scalar :: from_i32 ( 0 ) )
363+ }
298364}
0 commit comments