Skip to content

Introduce a new io_buffer module. #229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/android/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::ops::{Deref, DerefMut};
use kernel::{
bindings,
bindings::*,
user_ptr::{ReadableFromBytes, WritableToBytes},
io_buffer::{ReadableFromBytes, WritableToBytes},
};

macro_rules! pub_no_prefix {
Expand Down
1 change: 1 addition & 0 deletions drivers/android/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use core::{
sync::atomic::{AtomicU64, Ordering},
};
use kernel::{
io_buffer::IoBufferWriter,
linked_list::{GetLinks, Links, List},
prelude::*,
sync::{Guard, LockedBy, Mutex, Ref, SpinLock},
Expand Down
1 change: 1 addition & 0 deletions drivers/android/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use core::{
use kernel::{
bindings, c_types,
file_operations::{File, FileOpener, FileOperations, IoctlCommand, IoctlHandler, PollTable},
io_buffer::{IoBufferReader, IoBufferWriter},
linked_list::List,
pages::Pages,
prelude::*,
Expand Down
1 change: 1 addition & 0 deletions drivers/android/rust_binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use alloc::{boxed::Box, sync::Arc};
use core::pin::Pin;
use kernel::{
cstr,
io_buffer::IoBufferWriter,
linked_list::{GetLinks, GetLinksWrapped, Links},
miscdev::Registration,
prelude::*,
Expand Down
1 change: 1 addition & 0 deletions drivers/android/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::{alloc::AllocError, mem::size_of, pin::Pin};
use kernel::{
bindings,
file_operations::{File, PollTable},
io_buffer::{IoBufferReader, IoBufferWriter},
linked_list::{GetLinks, Links, List},
prelude::*,
sync::{CondVar, Ref, SpinLock},
Expand Down
5 changes: 4 additions & 1 deletion drivers/android/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, Ordering};
use kernel::{bindings, linked_list::Links, prelude::*, sync::Ref, user_ptr::UserSlicePtrWriter};
use kernel::{
bindings, io_buffer::IoBufferWriter, linked_list::Links, prelude::*, sync::Ref,
user_ptr::UserSlicePtrWriter,
};

use crate::{
defs::*,
Expand Down
154 changes: 154 additions & 0 deletions rust/kernel/io_buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0

//! Buffers used in IO.

use crate::KernelResult;
use alloc::vec::Vec;
use core::mem::{size_of, MaybeUninit};

/// Represents a buffer to be read from during IO.
pub trait IoBufferReader {
/// Returns the number of bytes left to be read from the io buffer.
///
/// Note that even reading less than this number of bytes may fail.
fn len(&self) -> usize;

/// Returns `true` if no data is available in the io buffer.
fn is_empty(&self) -> bool {
self.len() == 0
}

/// Reads raw data from the io buffer into a raw kernel buffer.
///
/// # Safety
///
/// The output buffer must be valid.
unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> KernelResult;

/// Reads all data remaining in the io buffer.
///
/// Returns `EFAULT` if the address does not currently point to mapped, readable memory.
fn read_all(&mut self) -> KernelResult<Vec<u8>> {
let mut data = Vec::<u8>::new();
data.try_reserve_exact(self.len())?;
data.resize(self.len(), 0);

// SAFETY: The output buffer is valid as we just allocated it.
unsafe { self.read_raw(data.as_mut_ptr(), data.len())? };
Ok(data)
}

/// Reads a byte slice from the io buffer.
///
/// Returns `EFAULT` if the byte slice is bigger than the remaining size of the user slice or
/// if the address does not currently point to mapped, readable memory.
fn read_slice(&mut self, data: &mut [u8]) -> KernelResult {
// SAFETY: The output buffer is valid as it's coming from a live reference.
unsafe { self.read_raw(data.as_mut_ptr(), data.len()) }
}

/// Reads the contents of a plain old data (POD) type from the io buffer.
fn read<T: ReadableFromBytes>(&mut self) -> KernelResult<T> {
let mut out = MaybeUninit::<T>::uninit();
// SAFETY: The buffer is valid as it was just allocated.
unsafe { self.read_raw(out.as_mut_ptr() as _, size_of::<T>()) }?;
// SAFETY: We just initialised the data.
Ok(unsafe { out.assume_init() })
}
}

/// Represents a buffer to be written to during IO.
pub trait IoBufferWriter {
/// Returns the number of bytes left to be written into the io buffer.
///
/// Note that even writing less than this number of bytes may fail.
fn len(&self) -> usize;

/// Returns `true` if the io buffer cannot hold any additional data.
fn is_empty(&self) -> bool {
self.len() == 0
}

/// Writes zeroes to the io buffer.
///
/// Differently from the other write functions, `clear` will zero as much as it can and update
/// the writer internal state to reflect this. It will, however, return an error if it cannot
/// clear `len` bytes.
///
/// For example, if a caller requests that 100 bytes be cleared but a segfault happens after
/// 20 bytes, then EFAULT is returned and the writer is advanced by 20 bytes.
fn clear(&mut self, len: usize) -> KernelResult;

/// Writes a byte slice into the io buffer.
///
/// Returns `EFAULT` if the byte slice is bigger than the remaining size of the io buffer or if
/// the address does not currently point to mapped, writable memory.
fn write_slice(&mut self, data: &[u8]) -> KernelResult {
// SAFETY: The input buffer is valid as it's coming from a live reference.
unsafe { self.write_raw(data.as_ptr(), data.len()) }
}

/// Writes raw data to the io buffer from a raw kernel buffer.
///
/// # Safety
///
/// The input buffer must be valid.
unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> KernelResult;

/// Writes the contents of the given data into the io buffer.
fn write<T: WritableToBytes>(&mut self, data: &T) -> KernelResult<()> {
// SAFETY: The input buffer is valid as it's coming from a live
// reference to a type that implements `WritableToBytes`.
unsafe { self.write_raw(data as *const T as _, size_of::<T>()) }
}
}

/// Specifies that a type is safely readable from byte slices.
///
/// Not all types can be safely read from byte slices; examples from
/// <https://doc.rust-lang.org/reference/behavior-considered-undefined.html> include `bool`
/// that must be either `0` or `1`, and `char` that cannot be a surrogate or above `char::MAX`.
///
/// # Safety
///
/// Implementers must ensure that the type is made up only of types that can be safely read from
/// arbitrary byte sequences (e.g., `u32`, `u64`, etc.).
pub unsafe trait ReadableFromBytes {}

// SAFETY: All bit patterns are acceptable values of the types below.
unsafe impl ReadableFromBytes for u8 {}
unsafe impl ReadableFromBytes for u16 {}
unsafe impl ReadableFromBytes for u32 {}
unsafe impl ReadableFromBytes for u64 {}
unsafe impl ReadableFromBytes for usize {}
unsafe impl ReadableFromBytes for i8 {}
unsafe impl ReadableFromBytes for i16 {}
unsafe impl ReadableFromBytes for i32 {}
unsafe impl ReadableFromBytes for i64 {}
unsafe impl ReadableFromBytes for isize {}

/// Specifies that a type is safely writable to byte slices.
///
/// This means that we don't read undefined values (which leads to UB) in preparation for writing
/// to the byte slice. It also ensures that no potentially sensitive information is leaked into the
/// byte slices.
///
/// # Safety
///
/// A type must not include padding bytes and must be fully initialised to safely implement
/// [`WritableToBytes`] (i.e., it doesn't contain [`MaybeUninit`] fields). A composition of
/// writable types in a structure is not necessarily writable because it may result in padding
/// bytes.
pub unsafe trait WritableToBytes {}

// SAFETY: Initialised instances of the following types have no uninitialised portions.
unsafe impl WritableToBytes for u8 {}
unsafe impl WritableToBytes for u16 {}
unsafe impl WritableToBytes for u32 {}
unsafe impl WritableToBytes for u64 {}
unsafe impl WritableToBytes for usize {}
unsafe impl WritableToBytes for i8 {}
unsafe impl WritableToBytes for i16 {}
unsafe impl WritableToBytes for i32 {}
unsafe impl WritableToBytes for i64 {}
unsafe impl WritableToBytes for isize {}
1 change: 1 addition & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub mod sync;
#[cfg(CONFIG_SYSCTL)]
pub mod sysctl;

pub mod io_buffer;
mod types;
pub mod user_ptr;

Expand Down
9 changes: 6 additions & 3 deletions rust/kernel/pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
//!
//! TODO: This module is a work in progress.

use crate::{bindings, c_types, user_ptr::UserSlicePtrReader, Error, KernelResult, PAGE_SIZE};
use crate::{
bindings, c_types, io_buffer::IoBufferReader, user_ptr::UserSlicePtrReader, Error,
KernelResult, PAGE_SIZE,
};
use core::{marker::PhantomData, ptr};

extern "C" {
Expand Down Expand Up @@ -95,7 +98,7 @@ impl<const ORDER: u32> Pages<ORDER> {
///
/// Callers must ensure that the destination buffer is valid for the given length.
/// Additionally, if the raw buffer is intended to be recast, they must ensure that the data
/// can be safely cast; [`crate::user_ptr::ReadableFromBytes`] has more details about it.
/// can be safely cast; [`crate::io_buffer::ReadableFromBytes`] has more details about it.
pub unsafe fn read(&self, dest: *mut u8, offset: usize, len: usize) -> KernelResult {
// TODO: For now this only works on the first page.
let end = offset.checked_add(len).ok_or(Error::EINVAL)?;
Expand All @@ -114,7 +117,7 @@ impl<const ORDER: u32> Pages<ORDER> {
///
/// Callers must ensure that the buffer is valid for the given length. Additionally, if the
/// page is (or will be) mapped by userspace, they must ensure that no kernel data is leaked
/// through padding if it was cast from another type; [`crate::user_ptr::WritableToBytes`] has
/// through padding if it was cast from another type; [`crate::io_buffer::WritableToBytes`] has
/// more details about it.
pub unsafe fn write(&self, src: *const u8, offset: usize, len: usize) -> KernelResult {
// TODO: For now this only works on the first page.
Expand Down
11 changes: 6 additions & 5 deletions rust/kernel/sysctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ use core::mem;
use core::ptr;
use core::sync::atomic;

use crate::bindings;
use crate::c_types;
use crate::error;
use crate::types;
use crate::user_ptr::{UserSlicePtr, UserSlicePtrWriter};
use crate::{
bindings, c_types, error,
io_buffer::IoBufferWriter,
types,
user_ptr::{UserSlicePtr, UserSlicePtrWriter},
};

/// Sysctl storage.
pub trait SysctlStorage: Sync {
Expand Down
Loading