@@ -213,16 +213,16 @@ pub fn receive_with_heartbeat(rate: Duration) -> Message {
213
213
/// sending heartbeat signals) when another thread might be busy with reading
214
214
/// messages.
215
215
struct Connection {
216
- input : Mutex < std:: fs:: File > ,
217
- output : Mutex < std:: fs:: File > ,
216
+ input : Mutex < & ' static mut std:: fs:: File > ,
217
+ output : Mutex < & ' static mut std:: fs:: File > ,
218
218
}
219
219
220
220
lazy_static ! {
221
221
static ref CONNECTION : Connection = {
222
- let mut input = file_from_env_var( "FLEETSPEAK_COMMS_CHANNEL_INFD" ) ;
223
- let mut output = file_from_env_var( "FLEETSPEAK_COMMS_CHANNEL_OUTFD" ) ;
222
+ let input = file_from_env_var( "FLEETSPEAK_COMMS_CHANNEL_INFD" ) ;
223
+ let output = file_from_env_var( "FLEETSPEAK_COMMS_CHANNEL_OUTFD" ) ;
224
224
225
- crate :: io:: handshake( & mut input, & mut output)
225
+ crate :: io:: handshake( input, output)
226
226
. expect( "handshake failure" ) ;
227
227
228
228
log:: info!( target: "fleetspeak" , "handshake successful" ) ;
@@ -243,7 +243,7 @@ lazy_static! {
243
243
///
244
244
/// Any I/O error returned by the executed function indicates a fatal connection
245
245
/// failure and ends with a panic.
246
- fn execute < F , T > ( mutex : & Mutex < std:: fs:: File > , f : F ) -> T
246
+ fn execute < F , T > ( mutex : & Mutex < & mut std:: fs:: File > , f : F ) -> T
247
247
where
248
248
F : FnOnce ( & mut std:: fs:: File ) -> std:: io:: Result < T > ,
249
249
{
@@ -260,23 +260,91 @@ where
260
260
/// a valid file descriptor (in which case the library cannot be initialized and
261
261
/// the service is unlikely to work anyway).
262
262
///
263
+ /// This function returns a static mutable reference to ensure that the file is
264
+ /// never dropped.
265
+ ///
263
266
/// [`File`]: std::fs::File
264
- fn file_from_env_var ( var : & str ) -> std:: fs:: File {
267
+ fn file_from_env_var ( var : & str ) -> & ' static mut std:: fs:: File {
265
268
let fd = std:: env:: var ( var)
266
269
. expect ( & format ! ( "invalid variable `{}`" , var) )
267
270
. parse ( )
268
271
. expect ( & format ! ( "failed to parse file descriptor" ) ) ;
269
272
273
+ // We run inside a critical section to guarantee that between verifying the
274
+ // descriptor and using `std::File::from_raw_*` functions (see the comments
275
+ // below) nothing closes it inbetween.
276
+ let mutex = Mutex :: new ( ( ) ) ;
277
+ let guard = mutex. lock ( )
278
+ . unwrap ( ) ;
279
+
280
+ // SAFETY: `std::fs::File::from_raw_fd` requires the file descriptor to be
281
+ // valid and open. We verify this through `fcntl` and panic if the check
282
+ // fails. Because we run within a critical section we are sure that the file
283
+ // was not closed at the moment we wrap the raw descriptor.
284
+ //
285
+ // Note that the whole issue is more subtle than this. While we uphold the
286
+ // safety requirements of `from_raw_fd`, we cannot guarantee that we are
287
+ // exclusive owner of the descriptor or that the descriptor remains open
288
+ // throughout the entirety of the process lifetime which might lead to other
289
+ // kinds of undefined behaviour. As an additional safety measure we return
290
+ // a static mutable reference to ensure that the file destructor is never
291
+ // called. See the discussion in [1].
292
+ //
293
+ // [1]: https://github.com/rust-lang/unsafe-code-guidelines/issues/434
270
294
#[ cfg( target_family = "unix" ) ]
271
- unsafe {
295
+ let file = unsafe {
296
+ if libc:: fcntl ( fd, libc:: F_GETFD ) == -1 {
297
+ let error = std:: io:: Error :: last_os_error ( ) ;
298
+ panic ! ( "invalid file descriptor '{fd}': {error}" ) ;
299
+ }
300
+
272
301
std:: os:: unix:: io:: FromRawFd :: from_raw_fd ( fd)
273
- }
302
+ } ;
274
303
304
+ // SAFETY: `std::fs::File::from_raw_handle` requires the file handle to be
305
+ // valid, open and closable via `CloseHandle`. We verify this through a call
306
+ // to `GetFileType`: if the call fails or returns an unexpected file type,
307
+ // we panic. We expect the type to be a named pipe: this is what Fleetspeak
308
+ // should pass as and it is closable via `CloseHandle` [1] as required. We
309
+ // run within a critical section we are sure that the file was not closed at
310
+ // the moment we wrap the raw handle.
311
+ //
312
+ // See also remarks in the comment for the Unix branch of this method.
313
+ //
314
+ // [1]: https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle#remarks
275
315
#[ cfg( target_family = "windows" ) ]
276
- unsafe {
277
- // We use `identity` to specify the type for the `parse` call above and
278
- // then cast it to an appropriate Windows-specific pointer type.
279
- let handle = std:: convert:: identity :: < usize > ( fd) as _ ;
316
+ let file = unsafe {
317
+ use windows_sys:: Win32 :: {
318
+ Foundation :: * ,
319
+ Storage :: FileSystem :: * ,
320
+ } ;
321
+
322
+ // We use `identity` to specify the type for the `parse` call above.
323
+ let handle = std:: convert:: identity :: < HANDLE > ( fd) ;
324
+
325
+ // We fail both in case there is something wrong with the handle (in
326
+ // which case the call to `GetFileType` should return unknown file type
327
+ // and set the last error value) or the file type is not as expected.
328
+ let file_type = GetFileType ( handle) ;
329
+ if file_type != FILE_TYPE_PIPE {
330
+ let code = GetLastError ( ) ;
331
+ if code != NO_ERROR {
332
+ let error = std:: io:: Error :: from_raw_os_error ( code as i32 ) ;
333
+ panic ! ( "invalid file descriptor '{handle}': {error}" ) ;
334
+ } else {
335
+ panic ! ( "wrong type of file descriptor '{handle}': {file_type}" ) ;
336
+ }
337
+ }
338
+
339
+ // `HANDLE` type from `windows-sys` and from the standard library are
340
+ // different (one is a pointer and one is `isize`), so we have to cast
341
+ // between them.
342
+ let handle = handle as std:: os:: windows:: raw:: HANDLE ;
343
+
280
344
std:: os:: windows:: io:: FromRawHandle :: from_raw_handle ( handle)
281
- }
345
+ } ;
346
+
347
+ drop ( guard) ;
348
+
349
+ Box :: leak ( Box :: new ( file) )
282
350
}
0 commit comments