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
+
1
5
use std:: cell:: { OnceCell , RefCell } ;
2
6
use std:: collections:: VecDeque ;
3
7
use std:: io;
@@ -13,12 +17,13 @@ use crate::{concurrency::VClock, *};
13
17
/// be configured in the real system.
14
18
const MAX_SOCKETPAIR_BUFFER_CAPACITY : usize = 212992 ;
15
19
16
- /// Pair of connected sockets.
20
+ /// One end of a pair of connected unnamed sockets.
17
21
#[ 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
22
27
/// writing to. This is a weak reference because the other side may be closed before us; all
23
28
/// future writes will then trigger EPIPE.
24
29
peer_fd : OnceCell < WeakFileDescriptionRef > ,
@@ -37,13 +42,13 @@ impl Buffer {
37
42
}
38
43
}
39
44
40
- impl SocketPair {
45
+ impl AnonSocket {
41
46
fn peer_fd ( & self ) -> & WeakFileDescriptionRef {
42
47
self . peer_fd . get ( ) . unwrap ( )
43
48
}
44
49
}
45
50
46
- impl FileDescription for SocketPair {
51
+ impl FileDescription for AnonSocket {
47
52
fn name ( & self ) -> & ' static str {
48
53
"socketpair"
49
54
}
@@ -55,17 +60,25 @@ impl FileDescription for SocketPair {
55
60
let mut epoll_ready_events = EpollReadyEvents :: new ( ) ;
56
61
57
62
// 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.
60
69
epoll_ready_events. epollin = true ;
61
70
}
62
71
63
72
// Check if is writable.
64
73
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.
69
82
epoll_ready_events. epollout = true ;
70
83
}
71
84
} else {
@@ -108,7 +121,12 @@ impl FileDescription for SocketPair {
108
121
return Ok ( Ok ( 0 ) ) ;
109
122
}
110
123
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 ( ) ;
112
130
if readbuf. buf . is_empty ( ) {
113
131
if self . peer_fd ( ) . upgrade ( ) . is_none ( ) {
114
132
// Socketpair with no peer and empty buffer.
@@ -176,7 +194,13 @@ impl FileDescription for SocketPair {
176
194
// closed.
177
195
return Ok ( Err ( Error :: from ( ErrorKind :: BrokenPipe ) ) ) ;
178
196
} ;
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 ( ) ;
180
204
let data_size = writebuf. buf . len ( ) ;
181
205
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY . strict_sub ( data_size) ;
182
206
if available_space == 0 {
@@ -227,12 +251,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227
251
228
252
let mut is_sock_nonblock = false ;
229
253
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.
236
255
// SOCK_NONBLOCK only exists on Linux.
237
256
if this. tcx . sess . target . os == "linux" {
238
257
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> {
253
272
and AF_LOCAL are allowed",
254
273
domain
255
274
) ;
256
- } else if type_ != 0 {
275
+ } else if type_ != this . eval_libc_i32 ( "SOCK_STREAM" ) {
257
276
throw_unsup_format ! (
258
277
"socketpair: type {:#x} is unsupported, only SOCK_STREAM, \
259
278
SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
@@ -268,20 +287,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
268
287
269
288
// Generate file descriptions.
270
289
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 ( ) ) ) ,
273
292
peer_fd : OnceCell :: new ( ) ,
274
293
is_nonblock : is_sock_nonblock,
275
294
} ) ;
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 ( ) ) ) ,
278
297
peer_fd : OnceCell :: new ( ) ,
279
298
is_nonblock : is_sock_nonblock,
280
299
} ) ;
281
300
282
301
// 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 ( ) ;
285
304
286
305
// Insert the file description to the fd table, generating the file descriptors.
287
306
let sv0 = fds. insert ( fd0) ;
@@ -295,4 +314,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
295
314
296
315
Ok ( Scalar :: from_i32 ( 0 ) )
297
316
}
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
+ }
298
364
}
0 commit comments