Skip to content

Add some missing // SAFETY comments #475

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

Closed
Closed
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
5 changes: 4 additions & 1 deletion rust/kernel/miscdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl<T: FileOperations> Registration<T> {
open_data: T::OpenData,
opts: &Options<'_>,
) -> Result {
// SAFETY: We must ensure that we never move out of `this`.
// SAFETY: we don't move out of `this`.
let this = unsafe { self.get_unchecked_mut() };
if this.registered {
// Already registered.
Expand All @@ -170,6 +170,9 @@ impl<T: FileOperations> Registration<T> {
this.registered = true;
this.open_data.write(open_data);

// SAFETY: the `fops`, `name` and `minor` fields of `this.mdev` have
// been properly initialized above (the other fields are zeroed and will be
// initialized by `misc_register`).
let ret = unsafe { bindings::misc_register(&mut this.mdev) };
if ret < 0 {
// INVARIANT: `registered` is set back to `false` and the `open_data` is destructued.
Expand Down
24 changes: 20 additions & 4 deletions rust/kernel/pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<const ORDER: u32> Pages<ORDER> {
///
/// # Safety
///
/// Callers must ensure that the destination buffer is valid for the given length.
/// Callers must ensure that the destination buffer is valid for writes 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::io_buffer::ReadableFromBytes`] has more details about it.
pub unsafe fn read(&self, dest: *mut u8, offset: usize, len: usize) -> Result {
Expand All @@ -93,15 +93,23 @@ impl<const ORDER: u32> Pages<ORDER> {
}

let mapping = self.kmap(0).ok_or(Error::EINVAL)?;
unsafe { ptr::copy((mapping.ptr as *mut u8).add(offset), dest, len) };
// SAFETY: we just checked that `offset + len <= PAGE_SIZE`, so `mapping.ptr` and
// `mapping.ptr + offset` are in the same allocated object, the page we're
// reading from, with no overflow. Also, `offset <= PAGE_SIZE < isize::MAX`
// so `offset` can't overflow an `isize`.
let src = unsafe { (mapping.ptr as *mut u8).add(offset) };
// SAFETY: `src` is valid for reads from the type invariants, and `dest` is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... it is valid for reads because we check len <= PAGE_SIZE, no?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this was redundant with the previous comment

SAFETY: we just checked that offset + len <= PAGE_SIZE, so mapping.ptr and mapping.ptr + offset are in the same allocated object, the page we're reading from

I can repeat it if you prefer however.

// guaranteed by the caller to be valid for writes. Because we're copying `u8`s
// which have an alignment of 1, `src` and `dest` are always properly aligned.
unsafe { ptr::copy(src, dest, len) };
Ok(())
}

/// Maps the pages and writes into them from the given bufer.
///
/// # Safety
///
/// Callers must ensure that the buffer is valid for the given length. Additionally, if the
/// Callers must ensure that the source buffer is valid for reads 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::io_buffer::WritableToBytes`] has
/// more details about it.
Expand All @@ -113,7 +121,15 @@ impl<const ORDER: u32> Pages<ORDER> {
}

let mapping = self.kmap(0).ok_or(Error::EINVAL)?;
unsafe { ptr::copy(src, (mapping.ptr as *mut u8).add(offset), len) };
// SAFETY: we just checked that `offset + len <= PAGE_SIZE`, so `mapping.ptr` and
// `mapping.ptr + offset` are in the same allocated object, the page we're
// reading from, with no overflow. Also, `offset <= PAGE_SIZE < isize::MAX`
// so `offset` can't overflow an `isize`.
let dest = unsafe { (mapping.ptr as *mut u8).add(offset) };
// SAFETY: `src` is guaranteed by the caller to be valid for reads, and `dest` is
// valid for writes from the type invariants. Because we're copying `u8`s
// which have an alignment of 1, `src` and `dest` are always properly aligned.
unsafe { ptr::copy(src, dest, len) };
Ok(())
}

Expand Down
11 changes: 9 additions & 2 deletions rust/kernel/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ use core::fmt;
use crate::bindings;
use crate::c_types::{c_char, c_void};

// Called from `vsprintf` with format specifier `%pA`.
/// Called from `vsprintf` with format specifier `%pA`.
///
/// # Safety
///
/// The region between `buf` and `end` must be valid for writes.
/// `ptr` must point to a valid instance of `fmt::Arguments`.
#[no_mangle]
unsafe fn rust_fmt_argument(buf: *mut c_char, end: *mut c_char, ptr: *const c_void) -> *mut c_char {
use fmt::Write;
Expand All @@ -33,7 +38,7 @@ unsafe fn rust_fmt_argument(buf: *mut c_char, end: *mut c_char, ptr: *const c_vo
// `buf` goes past `end`.
let len_to_copy = cmp::min(buf_new, self.end).saturating_sub(self.buf);

// SAFETY: In any case, `buf` is non-null and properly aligned.
// SAFETY: The caller guarantees `buf` is non-null and properly aligned.
// If `len_to_copy` is non-zero, then we know `buf` has not past
// `end` yet and so is valid.
unsafe {
Expand All @@ -53,6 +58,7 @@ unsafe fn rust_fmt_argument(buf: *mut c_char, end: *mut c_char, ptr: *const c_vo
buf: buf as _,
end: end as _,
};
// SAFETY: the caller must guarantee that `ptr` is valid.
Copy link
Member

@ojeda ojeda Nov 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is an unsafe function, we can add this prerequisite to the function docs (in a Safety section), even if the function is not public; and then you can say here "ptr is valid due to the safety contract". You should also mention that ptr has to be an Arguments.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented the safety contract, though I'm not entirely sure it is correct

let _ = w.write_fmt(unsafe { *(ptr as *const fmt::Arguments<'_>) });
w.buf as _
}
Expand Down Expand Up @@ -132,6 +138,7 @@ pub unsafe fn call_printk(
args: fmt::Arguments<'_>,
) {
// `_printk` does not seem to fail in any path.
// SAFETY: this is safe by the safety contract.
unsafe {
bindings::_printk(
format_string.as_ptr() as _,
Expand Down