@@ -11,7 +11,8 @@ use crate::{
11
11
error:: { code:: * , Error , Result } ,
12
12
types:: { ARef , AlwaysRefCounted , NotThreadSafe , Opaque } ,
13
13
} ;
14
- use core:: { marker:: PhantomData , ptr} ;
14
+ use alloc:: boxed:: Box ;
15
+ use core:: { alloc:: AllocError , marker:: PhantomData , mem, ptr} ;
15
16
16
17
/// Flags associated with a [`File`].
17
18
pub mod flags {
@@ -257,6 +258,160 @@ impl Drop for FileDescriptorReservation {
257
258
}
258
259
}
259
260
261
+ /// Helper used for closing file descriptors in a way that is safe even if the file is currently
262
+ /// held using `fdget`.
263
+ ///
264
+ /// Additional motivation can be found in commit 80cd795630d6 ("binder: fix use-after-free due to
265
+ /// ksys_close() during fdget()") and in the comments on `binder_do_fd_close`.
266
+ pub struct DeferredFdCloser {
267
+ inner : Box < DeferredFdCloserInner > ,
268
+ }
269
+
270
+ /// SAFETY: This just holds an allocation with no real content, so there's no safety issue with
271
+ /// moving it across threads.
272
+ unsafe impl Send for DeferredFdCloser { }
273
+ unsafe impl Sync for DeferredFdCloser { }
274
+
275
+ #[ repr( C ) ]
276
+ struct DeferredFdCloserInner {
277
+ twork : mem:: MaybeUninit < bindings:: callback_head > ,
278
+ file : * mut bindings:: file ,
279
+ }
280
+
281
+ impl DeferredFdCloser {
282
+ /// Create a new [`DeferredFdCloser`].
283
+ pub fn new ( ) -> Result < Self , AllocError > {
284
+ Ok ( Self {
285
+ inner : Box :: try_new ( DeferredFdCloserInner {
286
+ twork : mem:: MaybeUninit :: uninit ( ) ,
287
+ file : core:: ptr:: null_mut ( ) ,
288
+ } ) ?,
289
+ } )
290
+ }
291
+
292
+ /// Schedule a task work that closes the file descriptor when this task returns to userspace.
293
+ ///
294
+ /// Fails if this is called from a context where we cannot run work when returning to
295
+ /// userspace. (E.g., from a kthread.)
296
+ pub fn close_fd ( self , fd : u32 ) -> Result < ( ) , DeferredFdCloseError > {
297
+ use bindings:: task_work_notify_mode_TWA_RESUME as TWA_RESUME ;
298
+
299
+ // In this method, we schedule the task work before closing the file. This is because
300
+ // scheduling a task work is fallible, and we need to know whether it will fail before we
301
+ // attempt to close the file.
302
+
303
+ // SAFETY: Getting a pointer to current is always safe.
304
+ let current = unsafe { bindings:: get_current ( ) } ;
305
+
306
+ // SAFETY: Accessing the `flags` field of `current` is always safe.
307
+ let is_kthread = ( unsafe { ( * current) . flags } & bindings:: PF_KTHREAD ) != 0 ;
308
+ if is_kthread {
309
+ return Err ( DeferredFdCloseError :: TaskWorkUnavailable ) ;
310
+ }
311
+
312
+ // This disables the destructor of the box, so the allocation is not cleaned up
313
+ // automatically below.
314
+ let inner = Box :: into_raw ( self . inner ) ;
315
+
316
+ // The `callback_head` field is first in the struct, so this cast correctly gives us a
317
+ // pointer to the field.
318
+ let callback_head = inner. cast :: < bindings:: callback_head > ( ) ;
319
+ // SAFETY: This pointer offset operation does not go out-of-bounds.
320
+ let file_field = unsafe { core:: ptr:: addr_of_mut!( ( * inner) . file) } ;
321
+
322
+ // SAFETY: The `callback_head` pointer is compatible with the `do_close_fd` method.
323
+ unsafe { bindings:: init_task_work ( callback_head, Some ( Self :: do_close_fd) ) } ;
324
+ // SAFETY: The `callback_head` pointer points at a valid and fully initialized task work
325
+ // that is ready to be scheduled.
326
+ //
327
+ // If the task work gets scheduled as-is, then it will be a no-op. However, we will update
328
+ // the file pointer below to tell it which file to fput.
329
+ let res = unsafe { bindings:: task_work_add ( current, callback_head, TWA_RESUME ) } ;
330
+
331
+ if res != 0 {
332
+ // SAFETY: Scheduling the task work failed, so we still have ownership of the box, so
333
+ // we may destroy it.
334
+ unsafe { drop ( Box :: from_raw ( inner) ) } ;
335
+
336
+ return Err ( DeferredFdCloseError :: TaskWorkUnavailable ) ;
337
+ }
338
+
339
+ // SAFETY: Just an FFI call. This is safe no matter what `fd` is.
340
+ let file = unsafe { bindings:: close_fd_get_file ( fd) } ;
341
+ if file. is_null ( ) {
342
+ // We don't clean up the task work since that might be expensive if the task work queue
343
+ // is long. Just let it execute and let it clean up for itself.
344
+ return Err ( DeferredFdCloseError :: BadFd ) ;
345
+ }
346
+
347
+ // SAFETY: The `file` pointer points at a valid file.
348
+ unsafe { bindings:: get_file ( file) } ;
349
+
350
+ // SAFETY: Due to the above `get_file`, even if the current task holds an `fdget` to
351
+ // this file right now, the refcount will not drop to zero until after it is released
352
+ // with `fdput`. This is because when using `fdget`, you must always use `fdput` before
353
+ // returning to userspace, and our task work runs after any `fdget` users have returned
354
+ // to userspace.
355
+ //
356
+ // Note: fl_owner_t is currently a void pointer.
357
+ unsafe { bindings:: filp_close ( file, ( * current) . files as bindings:: fl_owner_t ) } ;
358
+
359
+ // We update the file pointer that the task work is supposed to fput.
360
+ //
361
+ // SAFETY: Task works are executed on the current thread once we return to userspace, so
362
+ // this write is guaranteed to happen before `do_close_fd` is called, which means that a
363
+ // race is not possible here.
364
+ //
365
+ // It's okay to pass this pointer to the task work, since we just acquired a refcount with
366
+ // the previous call to `get_file`. Furthermore, the refcount will not drop to zero during
367
+ // an `fdget` call, since we defer the `fput` until after returning to userspace.
368
+ unsafe { * file_field = file } ;
369
+
370
+ Ok ( ( ) )
371
+ }
372
+
373
+ // SAFETY: This function is an implementation detail of `close_fd`, so its safety comments
374
+ // should be read in extension of that method.
375
+ unsafe extern "C" fn do_close_fd ( inner : * mut bindings:: callback_head ) {
376
+ // SAFETY: In `close_fd` we use this method together with a pointer that originates from a
377
+ // `Box<DeferredFdCloserInner>`, and we have just been given ownership of that allocation.
378
+ let inner = unsafe { Box :: from_raw ( inner as * mut DeferredFdCloserInner ) } ;
379
+ if !inner. file . is_null ( ) {
380
+ // SAFETY: This drops a refcount we acquired in `close_fd`. Since this callback runs in
381
+ // a task work after we return to userspace, it is guaranteed that the current thread
382
+ // doesn't hold this file with `fdget`, as `fdget` must be released before returning to
383
+ // userspace.
384
+ unsafe { bindings:: fput ( inner. file ) } ;
385
+ }
386
+ // Free the allocation.
387
+ drop ( inner) ;
388
+ }
389
+ }
390
+
391
+ /// Represents a failure to close an fd in a deferred manner.
392
+ #[ derive( Copy , Clone , Eq , PartialEq ) ]
393
+ pub enum DeferredFdCloseError {
394
+ /// Closing the fd failed because we were unable to schedule a task work.
395
+ TaskWorkUnavailable ,
396
+ /// Closing the fd failed because the fd does not exist.
397
+ BadFd ,
398
+ }
399
+
400
+ impl From < DeferredFdCloseError > for Error {
401
+ fn from ( err : DeferredFdCloseError ) -> Error {
402
+ match err {
403
+ DeferredFdCloseError :: TaskWorkUnavailable => ESRCH ,
404
+ DeferredFdCloseError :: BadFd => EBADF ,
405
+ }
406
+ }
407
+ }
408
+
409
+ impl core:: fmt:: Debug for DeferredFdCloseError {
410
+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
411
+ Error :: from ( * self ) . fmt ( f)
412
+ }
413
+ }
414
+
260
415
/// Represents the `EBADF` error code.
261
416
///
262
417
/// Used for methods that can only fail with `EBADF`.
0 commit comments