Skip to content

Commit d8c6b9c

Browse files
authored
Merge pull request #371 from wedsonaf/file-descriptor
binder: Add support for transferring file descriptors.
2 parents 62bc7aa + 42230f1 commit d8c6b9c

File tree

3 files changed

+173
-31
lines changed

3 files changed

+173
-31
lines changed

drivers/android/allocation.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// SPDX-License-Identifier: GPL-2.0
22

3-
use alloc::sync::Arc;
4-
use core::mem::{size_of, MaybeUninit};
5-
use kernel::{bindings, pages::Pages, prelude::*, user_ptr::UserSlicePtrReader, Error};
3+
use alloc::{boxed::Box, sync::Arc};
4+
use core::mem::{replace, size_of, MaybeUninit};
5+
use kernel::{
6+
bindings, linked_list::List, pages::Pages, prelude::*, user_ptr::UserSlicePtrReader, Error,
7+
};
68

79
use crate::{
810
defs::*,
911
node::NodeRef,
1012
process::{AllocationInfo, Process},
1113
thread::{BinderError, BinderResult},
14+
transaction::FileInfo,
1215
};
1316

1417
pub(crate) struct Allocation<'a> {
@@ -19,6 +22,7 @@ pub(crate) struct Allocation<'a> {
1922
pub(crate) process: &'a Process,
2023
allocation_info: Option<AllocationInfo>,
2124
free_on_drop: bool,
25+
file_list: List<Box<FileInfo>>,
2226
}
2327

2428
impl<'a> Allocation<'a> {
@@ -37,9 +41,18 @@ impl<'a> Allocation<'a> {
3741
pages,
3842
allocation_info: None,
3943
free_on_drop: true,
44+
file_list: List::new(),
4045
}
4146
}
4247

48+
pub(crate) fn take_file_list(&mut self) -> List<Box<FileInfo>> {
49+
replace(&mut self.file_list, List::new())
50+
}
51+
52+
pub(crate) fn add_file_info(&mut self, file: Box<FileInfo>) {
53+
self.file_list.push_back(file);
54+
}
55+
4356
fn iterate<T>(&self, mut offset: usize, mut size: usize, mut cb: T) -> Result
4457
where
4558
T: FnMut(&Pages<0>, usize, usize) -> Result,

drivers/android/thread.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: GPL-2.0
22

3-
use alloc::sync::Arc;
3+
use alloc::{boxed::Box, sync::Arc};
44
use core::{alloc::AllocError, mem::size_of, pin::Pin};
55
use kernel::{
66
bindings,
@@ -19,7 +19,7 @@ use crate::{
1919
defs::*,
2020
process::{AllocationInfo, Process},
2121
ptr_align,
22-
transaction::Transaction,
22+
transaction::{FileInfo, Transaction},
2323
DeliverCode, DeliverToRead, DeliverToReadListAdapter, Either,
2424
};
2525

@@ -376,7 +376,7 @@ impl Thread {
376376
fn translate_object(
377377
&self,
378378
index_offset: usize,
379-
view: &AllocationView,
379+
view: &mut AllocationView,
380380
allow_fds: bool,
381381
) -> BinderResult {
382382
let offset = view.alloc.read(index_offset)?;
@@ -386,7 +386,8 @@ impl Thread {
386386
BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => {
387387
let strong = header.type_ == BINDER_TYPE_BINDER;
388388
view.transfer_binder_object(offset, strong, |obj| {
389-
// SAFETY: The type is `BINDER_TYPE_{WEAK_}BINDER`, so `binder` is populated.
389+
// SAFETY: `binder` is a `binder_uintptr_t`; any bit pattern is a valid
390+
// representation.
390391
let ptr = unsafe { obj.__bindgen_anon_1.binder } as _;
391392
let cookie = obj.cookie as _;
392393
let flags = obj.flags as _;
@@ -398,7 +399,7 @@ impl Thread {
398399
BINDER_TYPE_WEAK_HANDLE | BINDER_TYPE_HANDLE => {
399400
let strong = header.type_ == BINDER_TYPE_HANDLE;
400401
view.transfer_binder_object(offset, strong, |obj| {
401-
// SAFETY: The type is `BINDER_TYPE_{WEAK_}HANDLE`, so `handle` is populated.
402+
// SAFETY: `handle` is a `u32`; any bit pattern is a valid representation.
402403
let handle = unsafe { obj.__bindgen_anon_1.handle } as _;
403404
self.process.get_node_from_handle(handle, strong)
404405
})?;
@@ -407,6 +408,15 @@ impl Thread {
407408
if !allow_fds {
408409
return Err(BinderError::new_failed());
409410
}
411+
412+
let obj = view.read::<bindings::binder_fd_object>(offset)?;
413+
// SAFETY: `fd` is a `u32`; any bit pattern is a valid representation.
414+
let fd = unsafe { obj.__bindgen_anon_1.fd };
415+
let file = File::from_fd(fd)?;
416+
let field_offset =
417+
kernel::offset_of!(bindings::binder_fd_object, __bindgen_anon_1.fd) as usize;
418+
let file_info = Box::try_new(FileInfo::new(file, offset + field_offset))?;
419+
view.alloc.add_file_info(file_info);
410420
}
411421
_ => pr_warn!("Unsupported binder object type: {:x}\n", header.type_),
412422
}
@@ -420,9 +430,9 @@ impl Thread {
420430
end: usize,
421431
allow_fds: bool,
422432
) -> BinderResult {
423-
let view = AllocationView::new(alloc, start);
433+
let mut view = AllocationView::new(alloc, start);
424434
for i in (start..end).step_by(size_of::<usize>()) {
425-
if let Err(err) = self.translate_object(i, &view, allow_fds) {
435+
if let Err(err) = self.translate_object(i, &mut view, allow_fds) {
426436
alloc.set_info(AllocationInfo { offsets: start..i });
427437
return Err(err);
428438
}
@@ -558,7 +568,7 @@ impl Thread {
558568
let completion = Arc::try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
559569
let process = orig.from.process.clone();
560570
let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
561-
let reply = Arc::try_new(Transaction::new_reply(self, process, tr, allow_fds)?)?;
571+
let reply = Transaction::new_reply(self, process, tr, allow_fds)?;
562572
self.inner.lock().push_work(completion);
563573
orig.from.deliver_reply(Either::Left(reply), &orig);
564574
Ok(())
@@ -592,7 +602,7 @@ impl Thread {
592602
let handle = unsafe { tr.target.handle };
593603
let node_ref = self.process.get_transaction_node(handle)?;
594604
let completion = Arc::try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
595-
let transaction = Arc::try_new(Transaction::new(node_ref, None, self, tr)?)?;
605+
let transaction = Transaction::new(node_ref, None, self, tr)?;
596606
self.inner.lock().push_work(completion);
597607
// TODO: Remove the completion on error?
598608
transaction.submit()?;
@@ -606,7 +616,7 @@ impl Thread {
606616
// could this happen?
607617
let top = self.top_of_transaction_stack()?;
608618
let completion = Arc::try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
609-
let transaction = Arc::try_new(Transaction::new(node_ref, top, self, tr)?)?;
619+
let transaction = Transaction::new(node_ref, top, self, tr)?;
610620

611621
// Check that the transaction stack hasn't changed while the lock was released, then update
612622
// it with the new transaction.

drivers/android/transaction.rs

Lines changed: 137 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
// SPDX-License-Identifier: GPL-2.0
22

3-
use alloc::sync::Arc;
4-
use core::sync::atomic::{AtomicBool, Ordering};
3+
use alloc::{boxed::Box, sync::Arc};
4+
use core::{
5+
pin::Pin,
6+
sync::atomic::{AtomicBool, Ordering},
7+
};
58
use kernel::{
6-
io_buffer::IoBufferWriter, linked_list::Links, prelude::*, sync::Ref,
7-
user_ptr::UserSlicePtrWriter, ScopeGuard,
9+
bindings,
10+
file::{File, FileDescriptorReservation},
11+
io_buffer::IoBufferWriter,
12+
linked_list::List,
13+
linked_list::{GetLinks, Links},
14+
prelude::*,
15+
sync::{Ref, SpinLock},
16+
user_ptr::UserSlicePtrWriter,
17+
Error, ScopeGuard,
818
};
919

1020
use crate::{
@@ -16,7 +26,12 @@ use crate::{
1626
DeliverToRead, Either,
1727
};
1828

29+
struct TransactionInner {
30+
file_list: List<Box<FileInfo>>,
31+
}
32+
1933
pub(crate) struct Transaction {
34+
inner: SpinLock<TransactionInner>,
2035
// TODO: Node should be released when the buffer is released.
2136
node_ref: Option<NodeRef>,
2237
stack_next: Option<Arc<Transaction>>,
@@ -37,13 +52,16 @@ impl Transaction {
3752
stack_next: Option<Arc<Transaction>>,
3853
from: &Arc<Thread>,
3954
tr: &BinderTransactionData,
40-
) -> BinderResult<Self> {
55+
) -> BinderResult<Arc<Self>> {
4156
let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0;
4257
let to = node_ref.node.owner.clone();
43-
let alloc = from.copy_transaction_data(&to, tr, allow_fds)?;
58+
let mut alloc = from.copy_transaction_data(&to, tr, allow_fds)?;
4459
let data_address = alloc.ptr;
60+
let file_list = alloc.take_file_list();
4561
alloc.keep_alive();
46-
Ok(Self {
62+
let mut tr = Arc::try_new(Self {
63+
// SAFETY: `spinlock_init` is called below.
64+
inner: unsafe { SpinLock::new(TransactionInner { file_list }) },
4765
node_ref: Some(node_ref),
4866
stack_next,
4967
from: from.clone(),
@@ -55,19 +73,28 @@ impl Transaction {
5573
offsets_size: tr.offsets_size as _,
5674
links: Links::new(),
5775
free_allocation: AtomicBool::new(true),
58-
})
76+
})?;
77+
78+
let mut_tr = Arc::get_mut(&mut tr).ok_or(Error::EINVAL)?;
79+
80+
// SAFETY: `inner` is pinned behind `Arc`.
81+
kernel::spinlock_init!(Pin::new_unchecked(&mut_tr.inner), "Transaction::inner");
82+
Ok(tr)
5983
}
6084

6185
pub(crate) fn new_reply(
6286
from: &Arc<Thread>,
6387
to: Ref<Process>,
6488
tr: &BinderTransactionData,
6589
allow_fds: bool,
66-
) -> BinderResult<Self> {
67-
let alloc = from.copy_transaction_data(&to, tr, allow_fds)?;
90+
) -> BinderResult<Arc<Self>> {
91+
let mut alloc = from.copy_transaction_data(&to, tr, allow_fds)?;
6892
let data_address = alloc.ptr;
93+
let file_list = alloc.take_file_list();
6994
alloc.keep_alive();
70-
Ok(Self {
95+
let mut tr = Arc::try_new(Self {
96+
// SAFETY: `spinlock_init` is called below.
97+
inner: unsafe { SpinLock::new(TransactionInner { file_list }) },
7198
node_ref: None,
7299
stack_next: None,
73100
from: from.clone(),
@@ -79,7 +106,13 @@ impl Transaction {
79106
offsets_size: tr.offsets_size as _,
80107
links: Links::new(),
81108
free_allocation: AtomicBool::new(true),
82-
})
109+
})?;
110+
111+
let mut_tr = Arc::get_mut(&mut tr).ok_or(Error::EINVAL)?;
112+
113+
// SAFETY: `inner` is pinned behind `Arc`.
114+
kernel::spinlock_init!(Pin::new_unchecked(&mut_tr.inner), "Transaction::inner");
115+
Ok(tr)
83116
}
84117

85118
/// Determines if the transaction is stacked on top of the given transaction.
@@ -136,6 +169,33 @@ impl Transaction {
136169
process.push_work(self)
137170
}
138171
}
172+
173+
/// Prepares the file list for delivery to the caller.
174+
fn prepare_file_list(&self) -> Result<List<Box<FileInfo>>> {
175+
// Get list of files that are being transferred as part of the transaction.
176+
let mut file_list = core::mem::replace(&mut self.inner.lock().file_list, List::new());
177+
178+
// If the list is non-empty, prepare the buffer.
179+
if !file_list.is_empty() {
180+
let alloc = self.to.buffer_get(self.data_address).ok_or(Error::ESRCH)?;
181+
let cleanup = ScopeGuard::new(|| {
182+
self.free_allocation.store(false, Ordering::Relaxed);
183+
});
184+
185+
let mut it = file_list.cursor_front_mut();
186+
while let Some(file_info) = it.current() {
187+
let reservation = FileDescriptorReservation::new(bindings::O_CLOEXEC)?;
188+
alloc.write(file_info.buffer_offset, &reservation.reserved_fd())?;
189+
file_info.reservation = Some(reservation);
190+
it.move_next();
191+
}
192+
193+
alloc.keep_alive();
194+
cleanup.dismiss();
195+
}
196+
197+
Ok(file_list)
198+
}
139199
}
140200

141201
impl DeliverToRead for Transaction {
@@ -145,9 +205,19 @@ impl DeliverToRead for Transaction {
145205
pub sender_euid: uid_t,
146206
*/
147207
let send_failed_reply = ScopeGuard::new(|| {
148-
let reply = Either::Right(BR_FAILED_REPLY);
149-
self.from.deliver_reply(reply, &self);
208+
if self.node_ref.is_some() && self.flags & TF_ONE_WAY == 0 {
209+
let reply = Either::Right(BR_FAILED_REPLY);
210+
self.from.deliver_reply(reply, &self);
211+
}
150212
});
213+
let mut file_list = if let Ok(list) = self.prepare_file_list() {
214+
list
215+
} else {
216+
// On failure to process the list, we send a reply back to the sender and ignore the
217+
// transaction on the recipient.
218+
return Ok(true);
219+
};
220+
151221
let mut tr = BinderTransactionData::default();
152222

153223
if let Some(nref) = &self.node_ref {
@@ -165,10 +235,6 @@ impl DeliverToRead for Transaction {
165235
tr.data.ptr.offsets = (self.data_address + ptr_align(self.data_size)) as _;
166236
}
167237

168-
// When `drop` is called, we don't want the allocation to be freed because it is now the
169-
// user's reponsibility to free it.
170-
self.free_allocation.store(false, Ordering::Relaxed);
171-
172238
let code = if self.node_ref.is_none() {
173239
BR_REPLY
174240
} else {
@@ -183,6 +249,27 @@ impl DeliverToRead for Transaction {
183249
// here on out.
184250
send_failed_reply.dismiss();
185251

252+
// Commit all files.
253+
{
254+
let mut it = file_list.cursor_front_mut();
255+
while let Some(file_info) = it.current() {
256+
if let Some(reservation) = file_info.reservation.take() {
257+
if let Some(file) = file_info.file.take() {
258+
reservation.commit(file);
259+
}
260+
}
261+
262+
it.move_next();
263+
}
264+
}
265+
266+
// When `drop` is called, we don't want the allocation to be freed because it is now the
267+
// user's reponsibility to free it.
268+
//
269+
// `drop` is guaranteed to see this relaxed store because `Arc` guarantess that everything
270+
// that happens when an object is referenced happens-before the eventual `drop`.
271+
self.free_allocation.store(false, Ordering::Relaxed);
272+
186273
// When this is not a reply and not an async transaction, update `current_transaction`. If
187274
// it's a reply, `current_transaction` has already been updated appropriately.
188275
if self.node_ref.is_some() && tr.flags & TF_ONE_WAY == 0 {
@@ -209,3 +296,35 @@ impl Drop for Transaction {
209296
}
210297
}
211298
}
299+
300+
pub(crate) struct FileInfo {
301+
links: Links<FileInfo>,
302+
303+
/// The file for which a descriptor will be created in the recipient process.
304+
file: Option<File>,
305+
306+
/// The file descriptor reservation on the recipient process.
307+
reservation: Option<FileDescriptorReservation>,
308+
309+
/// The offset in the buffer where the file descriptor is stored.
310+
buffer_offset: usize,
311+
}
312+
313+
impl FileInfo {
314+
pub(crate) fn new(file: File, buffer_offset: usize) -> Self {
315+
Self {
316+
file: Some(file),
317+
reservation: None,
318+
buffer_offset,
319+
links: Links::new(),
320+
}
321+
}
322+
}
323+
324+
impl GetLinks for FileInfo {
325+
type EntryType = Self;
326+
327+
fn get_links(data: &Self::EntryType) -> &Links<Self::EntryType> {
328+
&data.links
329+
}
330+
}

0 commit comments

Comments
 (0)