1
1
//! Linux `eventfd` implementation.
2
- //! Currently just a stub.
3
2
use std:: io;
3
+ use std:: io:: { Error , ErrorKind } ;
4
4
5
5
use rustc_target:: abi:: Endian ;
6
6
7
7
use crate :: shims:: unix:: * ;
8
- use crate :: * ;
8
+ use crate :: { concurrency :: VClock , * } ;
9
9
10
10
use self :: shims:: unix:: fd:: FileDescriptor ;
11
11
12
+ /// Minimum size of u8 array to hold u64 value.
13
+ const U64_MIN_ARRAY_SIZE : usize = 8 ;
14
+
15
+ /// Maximum value that the eventfd counter can hold.
16
+ const MAX_COUNTER : u64 = u64:: MAX - 1 ;
17
+
12
18
/// A kind of file descriptor created by `eventfd`.
13
19
/// The `Event` type isn't currently written to by `eventfd`.
14
20
/// The interface is meant to keep track of objects associated
@@ -20,7 +26,9 @@ use self::shims::unix::fd::FileDescriptor;
20
26
struct Event {
21
27
/// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the
22
28
/// kernel. This counter is initialized with the value specified in the argument initval.
23
- val : u64 ,
29
+ counter : u64 ,
30
+ is_nonblock : bool ,
31
+ clock : VClock ,
24
32
}
25
33
26
34
impl FileDescription for Event {
@@ -35,6 +43,38 @@ impl FileDescription for Event {
35
43
Ok ( Ok ( ( ) ) )
36
44
}
37
45
46
+ /// Read the counter in the buffer and return the counter if succeeded.
47
+ fn read < ' tcx > (
48
+ & mut self ,
49
+ _communicate_allowed : bool ,
50
+ bytes : & mut [ u8 ] ,
51
+ ecx : & mut MiriInterpCx < ' tcx > ,
52
+ ) -> InterpResult < ' tcx , io:: Result < usize > > {
53
+ // Check the size of slice, and return error only if the size of the slice < 8.
54
+ let Some ( bytes) = bytes. first_chunk_mut :: < U64_MIN_ARRAY_SIZE > ( ) else {
55
+ return Ok ( Err ( Error :: from ( ErrorKind :: InvalidInput ) ) ) ;
56
+ } ;
57
+ // Block when counter == 0.
58
+ if self . counter == 0 {
59
+ if self . is_nonblock {
60
+ return Ok ( Err ( Error :: from ( ErrorKind :: WouldBlock ) ) ) ;
61
+ } else {
62
+ //FIXME: blocking is not supported
63
+ throw_unsup_format ! ( "eventfd: blocking is unsupported" ) ;
64
+ }
65
+ } else {
66
+ // Prevent false alarm in data race detection when doing synchronisation via eventfd.
67
+ ecx. acquire_clock ( & self . clock ) ;
68
+ // Return the counter in the host endianness using the buffer provided by caller.
69
+ * bytes = match ecx. tcx . sess . target . endian {
70
+ Endian :: Little => self . counter . to_le_bytes ( ) ,
71
+ Endian :: Big => self . counter . to_be_bytes ( ) ,
72
+ } ;
73
+ self . counter = 0 ;
74
+ return Ok ( Ok ( U64_MIN_ARRAY_SIZE ) ) ;
75
+ }
76
+ }
77
+
38
78
/// A write call adds the 8-byte integer value supplied in
39
79
/// its buffer (in native endianness) to the counter. The maximum value that may be
40
80
/// stored in the counter is the largest unsigned 64-bit value
@@ -53,16 +93,37 @@ impl FileDescription for Event {
53
93
bytes : & [ u8 ] ,
54
94
ecx : & mut MiriInterpCx < ' tcx > ,
55
95
) -> InterpResult < ' tcx , io:: Result < usize > > {
56
- let bytes: [ u8 ; 8 ] = bytes. try_into ( ) . unwrap ( ) ; // FIXME fail gracefully when this has the wrong size
57
- // Convert from target endianness to host endianness.
96
+ // Check the size of slice, and return error only if the size of the slice < 8.
97
+ let Some ( bytes) = bytes. first_chunk :: < U64_MIN_ARRAY_SIZE > ( ) else {
98
+ return Ok ( Err ( Error :: from ( ErrorKind :: InvalidInput ) ) ) ;
99
+ } ;
100
+ // Convert from bytes to int according to host endianness.
58
101
let num = match ecx. tcx . sess . target . endian {
59
- Endian :: Little => u64:: from_le_bytes ( bytes) ,
60
- Endian :: Big => u64:: from_be_bytes ( bytes) ,
102
+ Endian :: Little => u64:: from_le_bytes ( * bytes) ,
103
+ Endian :: Big => u64:: from_be_bytes ( * bytes) ,
104
+ } ;
105
+ // u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
106
+ if num == u64:: MAX {
107
+ return Ok ( Err ( Error :: from ( ErrorKind :: InvalidInput ) ) ) ;
108
+ }
109
+ // If the addition does not let the counter to exceed the maximum value, update the counter.
110
+ // Else, block.
111
+ match self . counter . checked_add ( num) {
112
+ Some ( new_count @ 0 ..=MAX_COUNTER ) => {
113
+ // Prevent false alarm in data race detection when doing synchronisation via eventfd.
114
+ self . clock . join ( & ecx. release_clock ( ) . unwrap ( ) ) ;
115
+ self . counter = new_count;
116
+ }
117
+ None | Some ( u64:: MAX ) => {
118
+ if self . is_nonblock {
119
+ return Ok ( Err ( Error :: from ( ErrorKind :: WouldBlock ) ) ) ;
120
+ } else {
121
+ //FIXME: blocking is not supported
122
+ throw_unsup_format ! ( "eventfd: blocking is unsupported" ) ;
123
+ }
124
+ }
61
125
} ;
62
- // FIXME handle blocking when addition results in exceeding the max u64 value
63
- // or fail with EAGAIN if the file descriptor is nonblocking.
64
- self . val = self . val . checked_add ( num) . unwrap ( ) ;
65
- Ok ( Ok ( 8 ) )
126
+ Ok ( Ok ( U64_MIN_ARRAY_SIZE ) )
66
127
}
67
128
}
68
129
@@ -87,27 +148,41 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
87
148
fn eventfd ( & mut self , val : & OpTy < ' tcx > , flags : & OpTy < ' tcx > ) -> InterpResult < ' tcx , Scalar > {
88
149
let this = self . eval_context_mut ( ) ;
89
150
151
+ // eventfd is Linux specific.
152
+ this. assert_target_os ( "linux" , "eventfd" ) ;
153
+
90
154
let val = this. read_scalar ( val) ?. to_u32 ( ) ?;
91
- let flags = this. read_scalar ( flags) ?. to_i32 ( ) ?;
155
+ let mut flags = this. read_scalar ( flags) ?. to_i32 ( ) ?;
92
156
93
157
let efd_cloexec = this. eval_libc_i32 ( "EFD_CLOEXEC" ) ;
94
158
let efd_nonblock = this. eval_libc_i32 ( "EFD_NONBLOCK" ) ;
95
159
let efd_semaphore = this. eval_libc_i32 ( "EFD_SEMAPHORE" ) ;
96
160
97
- if flags & ( efd_cloexec | efd_nonblock | efd_semaphore) != flags {
98
- throw_unsup_format ! ( "eventfd: flag {flags:#x} is unsupported" ) ;
161
+ if flags & efd_semaphore == efd_semaphore {
162
+ throw_unsup_format ! ( "eventfd: EFD_SEMAPHORE is unsupported" ) ;
99
163
}
164
+
165
+ let mut is_nonblock = false ;
166
+ // Unload the flag that we support.
167
+ // After unloading, flags != 0 means other flags are used.
100
168
if flags & efd_cloexec == efd_cloexec {
101
- // cloexec does nothing as we don't support `exec`
169
+ flags &= !efd_cloexec ;
102
170
}
103
171
if flags & efd_nonblock == efd_nonblock {
104
- // FIXME remember the nonblock flag
172
+ flags &= !efd_nonblock;
173
+ is_nonblock = true ;
105
174
}
106
- if flags & efd_semaphore == efd_semaphore {
107
- throw_unsup_format ! ( "eventfd: EFD_SEMAPHORE is unsupported" ) ;
175
+ if flags != 0 {
176
+ let einval = this. eval_libc ( "EINVAL" ) ;
177
+ this. set_last_error ( einval) ?;
178
+ return Ok ( Scalar :: from_i32 ( -1 ) ) ;
108
179
}
109
180
110
- let fd = this. machine . fds . insert_fd ( FileDescriptor :: new ( Event { val : val. into ( ) } ) ) ;
181
+ let fd = this. machine . fds . insert_fd ( FileDescriptor :: new ( Event {
182
+ counter : val. into ( ) ,
183
+ is_nonblock,
184
+ clock : VClock :: default ( ) ,
185
+ } ) ) ;
111
186
Ok ( Scalar :: from_i32 ( fd) )
112
187
}
113
188
}
0 commit comments