summaryrefslogtreecommitdiff
path: root/drivers/android/binder/allocation.rs
blob: 7f65a9c3a0e58e07a7e6d4e7d7b185f73fb1aab8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
// SPDX-License-Identifier: GPL-2.0

// Copyright (C) 2025 Google LLC.

use core::mem::{size_of, size_of_val, MaybeUninit};
use core::ops::Range;

use kernel::{
    bindings,
    fs::file::{File, FileDescriptorReservation},
    prelude::*,
    sync::{aref::ARef, Arc},
    transmute::{AsBytes, FromBytes},
    uaccess::UserSliceReader,
    uapi,
};

use crate::{
    deferred_close::DeferredFdCloser,
    defs::*,
    node::{Node, NodeRef},
    process::Process,
    DArc,
};

#[derive(Default)]
pub(crate) struct AllocationInfo {
    /// Range within the allocation where we can find the offsets to the object descriptors.
    pub(crate) offsets: Option<Range<usize>>,
    /// The target node of the transaction this allocation is associated to.
    /// Not set for replies.
    pub(crate) target_node: Option<NodeRef>,
    /// When this allocation is dropped, call `pending_oneway_finished` on the node.
    ///
    /// This is used to serialize oneway transaction on the same node. Binder guarantees that
    /// oneway transactions to the same node are delivered sequentially in the order they are sent.
    pub(crate) oneway_node: Option<DArc<Node>>,
    /// Zero the data in the buffer on free.
    pub(crate) clear_on_free: bool,
    /// List of files embedded in this transaction.
    file_list: FileList,
}

/// Represents an allocation that the kernel is currently using.
///
/// When allocations are idle, the range allocator holds the data related to them.
///
/// # Invariants
///
/// This allocation corresponds to an allocation in the range allocator, so the relevant pages are
/// marked in use in the page range.
pub(crate) struct Allocation {
    pub(crate) offset: usize,
    size: usize,
    pub(crate) ptr: usize,
    pub(crate) process: Arc<Process>,
    allocation_info: Option<AllocationInfo>,
    free_on_drop: bool,
    pub(crate) oneway_spam_detected: bool,
    #[allow(dead_code)]
    pub(crate) debug_id: usize,
}

impl Allocation {
    pub(crate) fn new(
        process: Arc<Process>,
        debug_id: usize,
        offset: usize,
        size: usize,
        ptr: usize,
        oneway_spam_detected: bool,
    ) -> Self {
        Self {
            process,
            offset,
            size,
            ptr,
            debug_id,
            oneway_spam_detected,
            allocation_info: None,
            free_on_drop: true,
        }
    }

    fn size_check(&self, offset: usize, size: usize) -> Result {
        let overflow_fail = offset.checked_add(size).is_none();
        let cmp_size_fail = offset.wrapping_add(size) > self.size;
        if overflow_fail || cmp_size_fail {
            return Err(EFAULT);
        }
        Ok(())
    }

    pub(crate) fn copy_into(
        &self,
        reader: &mut UserSliceReader,
        offset: usize,
        size: usize,
    ) -> Result {
        self.size_check(offset, size)?;

        // SAFETY: While this object exists, the range allocator will keep the range allocated, and
        // in turn, the pages will be marked as in use.
        unsafe {
            self.process
                .pages
                .copy_from_user_slice(reader, self.offset + offset, size)
        }
    }

    pub(crate) fn read<T: FromBytes>(&self, offset: usize) -> Result<T> {
        self.size_check(offset, size_of::<T>())?;

        // SAFETY: While this object exists, the range allocator will keep the range allocated, and
        // in turn, the pages will be marked as in use.
        unsafe { self.process.pages.read(self.offset + offset) }
    }

    pub(crate) fn write<T: ?Sized>(&self, offset: usize, obj: &T) -> Result {
        self.size_check(offset, size_of_val::<T>(obj))?;

        // SAFETY: While this object exists, the range allocator will keep the range allocated, and
        // in turn, the pages will be marked as in use.
        unsafe { self.process.pages.write(self.offset + offset, obj) }
    }

    pub(crate) fn fill_zero(&self) -> Result {
        // SAFETY: While this object exists, the range allocator will keep the range allocated, and
        // in turn, the pages will be marked as in use.
        unsafe { self.process.pages.fill_zero(self.offset, self.size) }
    }

    pub(crate) fn keep_alive(mut self) {
        self.process
            .buffer_make_freeable(self.offset, self.allocation_info.take());
        self.free_on_drop = false;
    }

    pub(crate) fn set_info(&mut self, info: AllocationInfo) {
        self.allocation_info = Some(info);
    }

    pub(crate) fn get_or_init_info(&mut self) -> &mut AllocationInfo {
        self.allocation_info.get_or_insert_with(Default::default)
    }

    pub(crate) fn set_info_offsets(&mut self, offsets: Range<usize>) {
        self.get_or_init_info().offsets = Some(offsets);
    }

    pub(crate) fn set_info_oneway_node(&mut self, oneway_node: DArc<Node>) {
        self.get_or_init_info().oneway_node = Some(oneway_node);
    }

    pub(crate) fn set_info_clear_on_drop(&mut self) {
        self.get_or_init_info().clear_on_free = true;
    }

    pub(crate) fn set_info_target_node(&mut self, target_node: NodeRef) {
        self.get_or_init_info().target_node = Some(target_node);
    }

    /// Reserve enough space to push at least `num_fds` fds.
    pub(crate) fn info_add_fd_reserve(&mut self, num_fds: usize) -> Result {
        self.get_or_init_info()
            .file_list
            .files_to_translate
            .reserve(num_fds, GFP_KERNEL)?;

        Ok(())
    }

    pub(crate) fn info_add_fd(
        &mut self,
        file: ARef<File>,
        buffer_offset: usize,
        close_on_free: bool,
    ) -> Result {
        self.get_or_init_info().file_list.files_to_translate.push(
            FileEntry {
                file,
                buffer_offset,
                close_on_free,
            },
            GFP_KERNEL,
        )?;

        Ok(())
    }

    pub(crate) fn set_info_close_on_free(&mut self, cof: FdsCloseOnFree) {
        self.get_or_init_info().file_list.close_on_free = cof.0;
    }

    pub(crate) fn translate_fds(&mut self) -> Result<TranslatedFds> {
        let file_list = match self.allocation_info.as_mut() {
            Some(info) => &mut info.file_list,
            None => return Ok(TranslatedFds::new()),
        };

        let files = core::mem::take(&mut file_list.files_to_translate);

        let num_close_on_free = files.iter().filter(|entry| entry.close_on_free).count();
        let mut close_on_free = KVec::with_capacity(num_close_on_free, GFP_KERNEL)?;

        let mut reservations = KVec::with_capacity(files.len(), GFP_KERNEL)?;
        for file_info in files {
            let res = FileDescriptorReservation::get_unused_fd_flags(bindings::O_CLOEXEC)?;
            let fd = res.reserved_fd();
            self.write::<u32>(file_info.buffer_offset, &fd)?;

            reservations.push(
                Reservation {
                    res,
                    file: file_info.file,
                },
                GFP_KERNEL,
            )?;
            if file_info.close_on_free {
                close_on_free.push(fd, GFP_KERNEL)?;
            }
        }

        Ok(TranslatedFds {
            reservations,
            close_on_free: FdsCloseOnFree(close_on_free),
        })
    }

    /// Should the looper return to userspace when freeing this allocation?
    pub(crate) fn looper_need_return_on_free(&self) -> bool {
        // Closing fds involves pushing task_work for execution when we return to userspace. Hence,
        // we should return to userspace asap if we are closing fds.
        match self.allocation_info {
            Some(ref info) => !info.file_list.close_on_free.is_empty(),
            None => false,
        }
    }
}

impl Drop for Allocation {
    fn drop(&mut self) {
        if !self.free_on_drop {
            return;
        }

        if let Some(mut info) = self.allocation_info.take() {
            if let Some(oneway_node) = info.oneway_node.as_ref() {
                oneway_node.pending_oneway_finished();
            }

            info.target_node = None;

            if let Some(offsets) = info.offsets.clone() {
                let view = AllocationView::new(self, offsets.start);
                for i in offsets.step_by(size_of::<usize>()) {
                    if view.cleanup_object(i).is_err() {
                        pr_warn!("Error cleaning up object at offset {}\n", i)
                    }
                }
            }

            for &fd in &info.file_list.close_on_free {
                let closer = match DeferredFdCloser::new(GFP_KERNEL) {
                    Ok(closer) => closer,
                    Err(kernel::alloc::AllocError) => {
                        // Ignore allocation failures.
                        break;
                    }
                };

                // Here, we ignore errors. The operation can fail if the fd is not valid, or if the
                // method is called from a kthread. However, this is always called from a syscall,
                // so the latter case cannot happen, and we don't care about the first case.
                let _ = closer.close_fd(fd);
            }

            if info.clear_on_free {
                if let Err(e) = self.fill_zero() {
                    pr_warn!("Failed to clear data on free: {:?}", e);
                }
            }
        }

        self.process.buffer_raw_free(self.ptr);
    }
}

/// A wrapper around `Allocation` that is being created.
///
/// If the allocation is destroyed while wrapped in this wrapper, then the allocation will be
/// considered to be part of a failed transaction. Successful transactions avoid that by calling
/// `success`, which skips the destructor.
#[repr(transparent)]
pub(crate) struct NewAllocation(pub(crate) Allocation);

impl NewAllocation {
    pub(crate) fn success(self) -> Allocation {
        // This skips the destructor.
        //
        // SAFETY: This type is `#[repr(transparent)]`, so the layout matches.
        unsafe { core::mem::transmute(self) }
    }
}

impl core::ops::Deref for NewAllocation {
    type Target = Allocation;
    fn deref(&self) -> &Allocation {
        &self.0
    }
}

impl core::ops::DerefMut for NewAllocation {
    fn deref_mut(&mut self) -> &mut Allocation {
        &mut self.0
    }
}

/// A view into the beginning of an allocation.
///
/// All attempts to read or write outside of the view will fail. To intentionally access outside of
/// this view, use the `alloc` field of this struct directly.
pub(crate) struct AllocationView<'a> {
    pub(crate) alloc: &'a mut Allocation,
    limit: usize,
}

impl<'a> AllocationView<'a> {
    pub(crate) fn new(alloc: &'a mut Allocation, limit: usize) -> Self {
        AllocationView { alloc, limit }
    }

    pub(crate) fn read<T: FromBytes>(&self, offset: usize) -> Result<T> {
        if offset.checked_add(size_of::<T>()).ok_or(EINVAL)? > self.limit {
            return Err(EINVAL);
        }
        self.alloc.read(offset)
    }

    pub(crate) fn write<T: AsBytes>(&self, offset: usize, obj: &T) -> Result {
        if offset.checked_add(size_of::<T>()).ok_or(EINVAL)? > self.limit {
            return Err(EINVAL);
        }
        self.alloc.write(offset, obj)
    }

    pub(crate) fn copy_into(
        &self,
        reader: &mut UserSliceReader,
        offset: usize,
        size: usize,
    ) -> Result {
        if offset.checked_add(size).ok_or(EINVAL)? > self.limit {
            return Err(EINVAL);
        }
        self.alloc.copy_into(reader, offset, size)
    }

    pub(crate) fn transfer_binder_object(
        &self,
        offset: usize,
        obj: &uapi::flat_binder_object,
        strong: bool,
        node_ref: NodeRef,
    ) -> Result {
        let mut newobj = FlatBinderObject::default();
        let node = node_ref.node.clone();
        if Arc::ptr_eq(&node_ref.node.owner, &self.alloc.process) {
            // The receiving process is the owner of the node, so send it a binder object (instead
            // of a handle).
            let (ptr, cookie) = node.get_id();
            newobj.hdr.type_ = if strong {
                BINDER_TYPE_BINDER
            } else {
                BINDER_TYPE_WEAK_BINDER
            };
            newobj.flags = obj.flags;
            newobj.__bindgen_anon_1.binder = ptr as _;
            newobj.cookie = cookie as _;
            self.write(offset, &newobj)?;
            // Increment the user ref count on the node. It will be decremented as part of the
            // destruction of the buffer, when we see a binder or weak-binder object.
            node.update_refcount(true, 1, strong);
        } else {
            // The receiving process is different from the owner, so we need to insert a handle to
            // the binder object.
            let handle = self
                .alloc
                .process
                .as_arc_borrow()
                .insert_or_update_handle(node_ref, false)?;
            newobj.hdr.type_ = if strong {
                BINDER_TYPE_HANDLE
            } else {
                BINDER_TYPE_WEAK_HANDLE
            };
            newobj.flags = obj.flags;
            newobj.__bindgen_anon_1.handle = handle;
            if self.write(offset, &newobj).is_err() {
                // Decrement ref count on the handle we just created.
                let _ = self
                    .alloc
                    .process
                    .as_arc_borrow()
                    .update_ref(handle, false, strong);
                return Err(EINVAL);
            }
        }

        Ok(())
    }

    fn cleanup_object(&self, index_offset: usize) -> Result {
        let offset = self.alloc.read(index_offset)?;
        let header = self.read::<BinderObjectHeader>(offset)?;
        match header.type_ {
            BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => {
                let obj = self.read::<FlatBinderObject>(offset)?;
                let strong = header.type_ == BINDER_TYPE_BINDER;
                // SAFETY: The type is `BINDER_TYPE_{WEAK_}BINDER`, so the `binder` field is
                // populated.
                let ptr = unsafe { obj.__bindgen_anon_1.binder };
                let cookie = obj.cookie;
                self.alloc.process.update_node(ptr, cookie, strong);
                Ok(())
            }
            BINDER_TYPE_WEAK_HANDLE | BINDER_TYPE_HANDLE => {
                let obj = self.read::<FlatBinderObject>(offset)?;
                let strong = header.type_ == BINDER_TYPE_HANDLE;
                // SAFETY: The type is `BINDER_TYPE_{WEAK_}HANDLE`, so the `handle` field is
                // populated.
                let handle = unsafe { obj.__bindgen_anon_1.handle };
                self.alloc
                    .process
                    .as_arc_borrow()
                    .update_ref(handle, false, strong)
            }
            _ => Ok(()),
        }
    }
}

/// A binder object as it is serialized.
///
/// # Invariants
///
/// All bytes must be initialized, and the value of `self.hdr.type_` must be one of the allowed
/// types.
#[repr(C)]
pub(crate) union BinderObject {
    hdr: uapi::binder_object_header,
    fbo: uapi::flat_binder_object,
    fdo: uapi::binder_fd_object,
    bbo: uapi::binder_buffer_object,
    fdao: uapi::binder_fd_array_object,
}

/// A view into a `BinderObject` that can be used in a match statement.
pub(crate) enum BinderObjectRef<'a> {
    Binder(&'a mut uapi::flat_binder_object),
    Handle(&'a mut uapi::flat_binder_object),
    Fd(&'a mut uapi::binder_fd_object),
    Ptr(&'a mut uapi::binder_buffer_object),
    Fda(&'a mut uapi::binder_fd_array_object),
}

impl BinderObject {
    pub(crate) fn read_from(reader: &mut UserSliceReader) -> Result<BinderObject> {
        let object = Self::read_from_inner(|slice| {
            let read_len = usize::min(slice.len(), reader.len());
            reader.clone_reader().read_slice(&mut slice[..read_len])?;
            Ok(())
        })?;

        // If we used a object type smaller than the largest object size, then we've read more
        // bytes than we needed to. However, we used `.clone_reader()` to avoid advancing the
        // original reader. Now, we call `skip` so that the caller's reader is advanced by the
        // right amount.
        //
        // The `skip` call fails if the reader doesn't have `size` bytes available. This could
        // happen if the type header corresponds to an object type that is larger than the rest of
        // the reader.
        //
        // Any extra bytes beyond the size of the object are inaccessible after this call, so
        // reading them again from the `reader` later does not result in TOCTOU bugs.
        reader.skip(object.size())?;

        Ok(object)
    }

    /// Use the provided reader closure to construct a `BinderObject`.
    ///
    /// The closure should write the bytes for the object into the provided slice.
    pub(crate) fn read_from_inner<R>(reader: R) -> Result<BinderObject>
    where
        R: FnOnce(&mut [u8; size_of::<BinderObject>()]) -> Result<()>,
    {
        let mut obj = MaybeUninit::<BinderObject>::zeroed();

        // SAFETY: The lengths of `BinderObject` and `[u8; size_of::<BinderObject>()]` are equal,
        // and the byte array has an alignment requirement of one, so the pointer cast is okay.
        // Additionally, `obj` was initialized to zeros, so the byte array will not be
        // uninitialized.
        (reader)(unsafe { &mut *obj.as_mut_ptr().cast() })?;

        // SAFETY: The entire object is initialized, so accessing this field is safe.
        let type_ = unsafe { obj.assume_init_ref().hdr.type_ };
        if Self::type_to_size(type_).is_none() {
            // The value of `obj.hdr_type_` was invalid.
            return Err(EINVAL);
        }

        // SAFETY: All bytes are initialized (since we zeroed them at the start) and we checked
        // that `self.hdr.type_` is one of the allowed types, so the type invariants are satisfied.
        unsafe { Ok(obj.assume_init()) }
    }

    pub(crate) fn as_ref(&mut self) -> BinderObjectRef<'_> {
        use BinderObjectRef::*;
        // SAFETY: The constructor ensures that all bytes of `self` are initialized, and all
        // variants of this union accept all initialized bit patterns.
        unsafe {
            match self.hdr.type_ {
                BINDER_TYPE_WEAK_BINDER | BINDER_TYPE_BINDER => Binder(&mut self.fbo),
                BINDER_TYPE_WEAK_HANDLE | BINDER_TYPE_HANDLE => Handle(&mut self.fbo),
                BINDER_TYPE_FD => Fd(&mut self.fdo),
                BINDER_TYPE_PTR => Ptr(&mut self.bbo),
                BINDER_TYPE_FDA => Fda(&mut self.fdao),
                // SAFETY: By the type invariant, the value of `self.hdr.type_` cannot have any
                // other value than the ones checked above.
                _ => core::hint::unreachable_unchecked(),
            }
        }
    }

    pub(crate) fn size(&self) -> usize {
        // SAFETY: The entire object is initialized, so accessing this field is safe.
        let type_ = unsafe { self.hdr.type_ };

        // SAFETY: The type invariants guarantee that the type field is correct.
        unsafe { Self::type_to_size(type_).unwrap_unchecked() }
    }

    fn type_to_size(type_: u32) -> Option<usize> {
        match type_ {
            BINDER_TYPE_WEAK_BINDER => Some(size_of::<uapi::flat_binder_object>()),
            BINDER_TYPE_BINDER => Some(size_of::<uapi::flat_binder_object>()),
            BINDER_TYPE_WEAK_HANDLE => Some(size_of::<uapi::flat_binder_object>()),
            BINDER_TYPE_HANDLE => Some(size_of::<uapi::flat_binder_object>()),
            BINDER_TYPE_FD => Some(size_of::<uapi::binder_fd_object>()),
            BINDER_TYPE_PTR => Some(size_of::<uapi::binder_buffer_object>()),
            BINDER_TYPE_FDA => Some(size_of::<uapi::binder_fd_array_object>()),
            _ => None,
        }
    }
}

#[derive(Default)]
struct FileList {
    files_to_translate: KVec<FileEntry>,
    close_on_free: KVec<u32>,
}

struct FileEntry {
    /// The file for which a descriptor will be created in the recipient process.
    file: ARef<File>,
    /// The offset in the buffer where the file descriptor is stored.
    buffer_offset: usize,
    /// Whether this fd should be closed when the allocation is freed.
    close_on_free: bool,
}

pub(crate) struct TranslatedFds {
    reservations: KVec<Reservation>,
    /// If commit is called, then these fds should be closed. (If commit is not called, then they
    /// shouldn't be closed.)
    close_on_free: FdsCloseOnFree,
}

struct Reservation {
    res: FileDescriptorReservation,
    file: ARef<File>,
}

impl TranslatedFds {
    pub(crate) fn new() -> Self {
        Self {
            reservations: KVec::new(),
            close_on_free: FdsCloseOnFree(KVec::new()),
        }
    }

    pub(crate) fn commit(self) -> FdsCloseOnFree {
        for entry in self.reservations {
            entry.res.fd_install(entry.file);
        }

        self.close_on_free
    }
}

pub(crate) struct FdsCloseOnFree(KVec<u32>);