@@ -11,7 +11,8 @@ use crate::{
11
11
error:: { code:: * , Error , Result } ,
12
12
types:: { ARef , AlwaysRefCounted , NotThreadSafe , Opaque } ,
13
13
} ;
14
- use core:: ptr;
14
+ use alloc:: boxed:: Box ;
15
+ use core:: { alloc:: AllocError , mem, ptr} ;
15
16
16
17
/// Flags associated with a [`File`].
17
18
pub mod flags {
@@ -318,6 +319,187 @@ impl Drop for FileDescriptorReservation {
318
319
}
319
320
}
320
321
322
+ /// Helper used for closing file descriptors in a way that is safe even if the file is currently
323
+ /// held using `fdget`.
324
+ ///
325
+ /// Additional motivation can be found in commit 80cd795630d6 ("binder: fix use-after-free due to
326
+ /// ksys_close() during fdget()") and in the comments on `binder_do_fd_close`.
327
+ pub struct DeferredFdCloser {
328
+ inner : Box < DeferredFdCloserInner > ,
329
+ }
330
+
331
+ /// SAFETY: This just holds an allocation with no real content, so there's no safety issue with
332
+ /// moving it across threads.
333
+ unsafe impl Send for DeferredFdCloser { }
334
+ unsafe impl Sync for DeferredFdCloser { }
335
+
336
+ /// # Invariants
337
+ ///
338
+ /// If the `file` pointer is non-null, then it points at a `struct file` and owns a refcount to
339
+ /// that file.
340
+ #[ repr( C ) ]
341
+ struct DeferredFdCloserInner {
342
+ twork : mem:: MaybeUninit < bindings:: callback_head > ,
343
+ file : * mut bindings:: file ,
344
+ }
345
+
346
+ impl DeferredFdCloser {
347
+ /// Create a new [`DeferredFdCloser`].
348
+ pub fn new ( ) -> Result < Self , AllocError > {
349
+ Ok ( Self {
350
+ // INVARIANT: The `file` pointer is null, so the type invariant does not apply.
351
+ inner : Box :: try_new ( DeferredFdCloserInner {
352
+ twork : mem:: MaybeUninit :: uninit ( ) ,
353
+ file : core:: ptr:: null_mut ( ) ,
354
+ } ) ?,
355
+ } )
356
+ }
357
+
358
+ /// Schedule a task work that closes the file descriptor when this task returns to userspace.
359
+ ///
360
+ /// Fails if this is called from a context where we cannot run work when returning to
361
+ /// userspace. (E.g., from a kthread.)
362
+ pub fn close_fd ( self , fd : u32 ) -> Result < ( ) , DeferredFdCloseError > {
363
+ use bindings:: task_work_notify_mode_TWA_RESUME as TWA_RESUME ;
364
+
365
+ // In this method, we schedule the task work before closing the file. This is because
366
+ // scheduling a task work is fallible, and we need to know whether it will fail before we
367
+ // attempt to close the file.
368
+
369
+ // Task works are not available on kthreads.
370
+ let current = crate :: current!( ) ;
371
+ if current. is_kthread ( ) {
372
+ return Err ( DeferredFdCloseError :: TaskWorkUnavailable ) ;
373
+ }
374
+
375
+ // Transfer ownership of the box's allocation to a raw pointer. This disables the
376
+ // destructor, so we must manually convert it back to a Box to drop it.
377
+ //
378
+ // Until we convert it back to a `Box`, there are no aliasing requirements on this
379
+ // pointer.
380
+ let inner = Box :: into_raw ( self . inner ) ;
381
+
382
+ // The `callback_head` field is first in the struct, so this cast correctly gives us a
383
+ // pointer to the field.
384
+ let callback_head = inner. cast :: < bindings:: callback_head > ( ) ;
385
+ // SAFETY: This pointer offset operation does not go out-of-bounds.
386
+ let file_field = unsafe { core:: ptr:: addr_of_mut!( ( * inner) . file) } ;
387
+
388
+ let current = current. as_raw ( ) ;
389
+
390
+ // SAFETY: This function currently has exclusive access to the `DeferredFdCloserInner`, so
391
+ // it is okay for us to perform unsynchronized writes to its `callback_head` field.
392
+ unsafe { bindings:: init_task_work ( callback_head, Some ( Self :: do_close_fd) ) } ;
393
+
394
+ // SAFETY: This inserts the `DeferredFdCloserInner` into the task workqueue for the current
395
+ // task. If this operation is successful, then this transfers exclusive ownership of the
396
+ // `callback_head` field to the C side until it calls `do_close_fd`, and we don't touch or
397
+ // invalidate the field during that time.
398
+ //
399
+ // When the C side calls `do_close_fd`, the safety requirements of that method are
400
+ // satisfied because when a task work is executed, the callback is given ownership of the
401
+ // pointer.
402
+ //
403
+ // The file pointer is currently null. If it is changed to be non-null before `do_close_fd`
404
+ // is called, then that change happens due to the write at the end of this function, and
405
+ // that write has a safety comment that explains why the refcount can be dropped when
406
+ // `do_close_fd` runs.
407
+ let res = unsafe { bindings:: task_work_add ( current, callback_head, TWA_RESUME ) } ;
408
+
409
+ if res != 0 {
410
+ // SAFETY: Scheduling the task work failed, so we still have ownership of the box, so
411
+ // we may destroy it.
412
+ unsafe { drop ( Box :: from_raw ( inner) ) } ;
413
+
414
+ return Err ( DeferredFdCloseError :: TaskWorkUnavailable ) ;
415
+ }
416
+
417
+ // This removes the fd from the fd table in `current`. The file is not fully closed until
418
+ // `filp_close` is called. We are given ownership of one refcount to the file.
419
+ //
420
+ // SAFETY: This is safe no matter what `fd` is. If the `fd` is valid (that is, if the
421
+ // pointer is non-null), then we call `filp_close` on the returned pointer as required by
422
+ // `file_close_fd`.
423
+ let file = unsafe { bindings:: file_close_fd ( fd) } ;
424
+ if file. is_null ( ) {
425
+ // We don't clean up the task work since that might be expensive if the task work queue
426
+ // is long. Just let it execute and let it clean up for itself.
427
+ return Err ( DeferredFdCloseError :: BadFd ) ;
428
+ }
429
+
430
+ // Acquire a second refcount to the file.
431
+ //
432
+ // SAFETY: The `file` pointer points at a file with a non-zero refcount.
433
+ unsafe { bindings:: get_file ( file) } ;
434
+
435
+ // This method closes the fd, consuming one of our two refcounts. There could be active
436
+ // light refcounts created from that fd, so we must ensure that the file has a positive
437
+ // refcount for the duration of those active light refcounts. We do that by holding on to
438
+ // the second refcount until the current task returns to userspace.
439
+ //
440
+ // SAFETY: The `file` pointer is valid. Passing `current->files` as the file table to close
441
+ // it in is correct, since we just got the `fd` from `file_close_fd` which also uses
442
+ // `current->files`.
443
+ //
444
+ // Note: fl_owner_t is currently a void pointer.
445
+ unsafe { bindings:: filp_close ( file, ( * current) . files as bindings:: fl_owner_t ) } ;
446
+
447
+ // We update the file pointer that the task work is supposed to fput. This transfers
448
+ // ownership of our last refcount.
449
+ //
450
+ // INVARIANT: This changes the `file` field of a `DeferredFdCloserInner` from null to
451
+ // non-null. This doesn't break the type invariant for `DeferredFdCloserInner` because we
452
+ // still own a refcount to the file, so we can pass ownership of that refcount to the
453
+ // `DeferredFdCloserInner`.
454
+ //
455
+ // When `do_close_fd` runs, it must be safe for it to `fput` the refcount. However, this is
456
+ // the case because all light refcounts that are associated with the fd we closed
457
+ // previously must be dropped when `do_close_fd`, since light refcounts must be dropped
458
+ // before returning to userspace.
459
+ //
460
+ // SAFETY: Task works are executed on the current thread right before we return to
461
+ // userspace, so this write is guaranteed to happen before `do_close_fd` is called, which
462
+ // means that a race is not possible here.
463
+ unsafe { * file_field = file } ;
464
+
465
+ Ok ( ( ) )
466
+ }
467
+
468
+ /// # Safety
469
+ ///
470
+ /// The provided pointer must point at the `twork` field of a `DeferredFdCloserInner` stored in
471
+ /// a `Box`, and the caller must pass exclusive ownership of that `Box`. Furthermore, if the
472
+ /// file pointer is non-null, then it must be okay to release the refcount by calling `fput`.
473
+ unsafe extern "C" fn do_close_fd ( inner : * mut bindings:: callback_head ) {
474
+ // SAFETY: The caller just passed us ownership of this box.
475
+ let inner = unsafe { Box :: from_raw ( inner. cast :: < DeferredFdCloserInner > ( ) ) } ;
476
+ if !inner. file . is_null ( ) {
477
+ // SAFETY: By the type invariants, we own a refcount to this file, and the caller
478
+ // guarantees that dropping the refcount now is okay.
479
+ unsafe { bindings:: fput ( inner. file ) } ;
480
+ }
481
+ // The allocation is freed when `inner` goes out of scope.
482
+ }
483
+ }
484
+
485
+ /// Represents a failure to close an fd in a deferred manner.
486
+ #[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
487
+ pub enum DeferredFdCloseError {
488
+ /// Closing the fd failed because we were unable to schedule a task work.
489
+ TaskWorkUnavailable ,
490
+ /// Closing the fd failed because the fd does not exist.
491
+ BadFd ,
492
+ }
493
+
494
+ impl From < DeferredFdCloseError > for Error {
495
+ fn from ( err : DeferredFdCloseError ) -> Error {
496
+ match err {
497
+ DeferredFdCloseError :: TaskWorkUnavailable => ESRCH ,
498
+ DeferredFdCloseError :: BadFd => EBADF ,
499
+ }
500
+ }
501
+ }
502
+
321
503
/// Represents the `EBADF` error code.
322
504
///
323
505
/// Used for methods that can only fail with `EBADF`.
0 commit comments