11//! Linux `eventfd` implementation.
2- //! Currently just a stub.
32use std:: io;
3+ use std:: io:: { Error , ErrorKind } ;
44
55use rustc_target:: abi:: Endian ;
66
77use crate :: shims:: unix:: * ;
8- use crate :: * ;
8+ use crate :: { concurrency :: VClock , * } ;
99
1010use self :: shims:: unix:: fd:: FileDescriptor ;
1111
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+
1218/// A kind of file descriptor created by `eventfd`.
1319/// The `Event` type isn't currently written to by `eventfd`.
1420/// The interface is meant to keep track of objects associated
@@ -20,7 +26,9 @@ use self::shims::unix::fd::FileDescriptor;
2026struct Event {
2127 /// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the
2228 /// 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 ,
2432}
2533
2634impl FileDescription for Event {
@@ -35,6 +43,38 @@ impl FileDescription for Event {
3543 Ok ( Ok ( ( ) ) )
3644 }
3745
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+
3878 /// A write call adds the 8-byte integer value supplied in
3979 /// its buffer (in native endianness) to the counter. The maximum value that may be
4080 /// stored in the counter is the largest unsigned 64-bit value
@@ -53,16 +93,37 @@ impl FileDescription for Event {
5393 bytes : & [ u8 ] ,
5494 ecx : & mut MiriInterpCx < ' tcx > ,
5595 ) -> 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.
58101 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+ }
61125 } ;
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 ) )
66127 }
67128}
68129
@@ -87,27 +148,41 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
87148 fn eventfd ( & mut self , val : & OpTy < ' tcx > , flags : & OpTy < ' tcx > ) -> InterpResult < ' tcx , Scalar > {
88149 let this = self . eval_context_mut ( ) ;
89150
151+ // eventfd is Linux specific.
152+ this. assert_target_os ( "linux" , "eventfd" ) ;
153+
90154 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 ( ) ?;
92156
93157 let efd_cloexec = this. eval_libc_i32 ( "EFD_CLOEXEC" ) ;
94158 let efd_nonblock = this. eval_libc_i32 ( "EFD_NONBLOCK" ) ;
95159 let efd_semaphore = this. eval_libc_i32 ( "EFD_SEMAPHORE" ) ;
96160
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" ) ;
99163 }
164+
165+ let mut is_nonblock = false ;
166+ // Unload the flag that we support.
167+ // After unloading, flags != 0 means other flags are used.
100168 if flags & efd_cloexec == efd_cloexec {
101- // cloexec does nothing as we don't support `exec`
169+ flags &= !efd_cloexec ;
102170 }
103171 if flags & efd_nonblock == efd_nonblock {
104- // FIXME remember the nonblock flag
172+ flags &= !efd_nonblock;
173+ is_nonblock = true ;
105174 }
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 ) ) ;
108179 }
109180
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+ } ) ) ;
111186 Ok ( Scalar :: from_i32 ( fd) )
112187 }
113188}
0 commit comments