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