Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions rust/bindings/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))]
#![allow(
clippy::all,
clippy::undocumented_unsafe_blocks,
missing_docs,
non_camel_case_types,
non_upper_case_globals,
Expand Down
32 changes: 18 additions & 14 deletions rust/kernel/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@ use crate::bindings;

struct KernelAllocator;

// SAFETY: this implementation meets `GlobalAlloc` safety invariants:
// - `kalloc` does not unwind
// - `kalloc` has no stricter safety requirements than those of `GlobalAlloc` itself
// - Allocating has no further side effects
unsafe impl GlobalAlloc for KernelAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// `krealloc()` is used instead of `kmalloc()` because the latter is
// an inline function and cannot be bound to as a result.
unsafe { bindings::krealloc(ptr::null(), layout.size(), bindings::GFP_KERNEL) as *mut u8 }
// SAFETY: `krealloc` is a FFI call with no invariants to meet
unsafe { bindings::krealloc(ptr::null(), layout.size(), bindings::GFP_KERNEL).cast() }
}

unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
unsafe {
bindings::kfree(ptr as *const core::ffi::c_void);
}
// SAFETY: `dealloc` has the invariant that `ptr` was allocated by this
// allocator. `kfree` has no additional invariants.
unsafe { bindings::kfree(ptr.cast()) };
}
}

Expand All @@ -33,32 +38,31 @@ static ALLOCATOR: KernelAllocator = KernelAllocator;
// Note that `#[no_mangle]` implies exported too, nowadays.
#[no_mangle]
fn __rust_alloc(size: usize, _align: usize) -> *mut u8 {
unsafe { bindings::krealloc(core::ptr::null(), size, bindings::GFP_KERNEL) as *mut u8 }
// SAFETY: `krealloc` is a FFI call with no invariants to meet
unsafe { bindings::krealloc(core::ptr::null(), size, bindings::GFP_KERNEL).cast() }
}

#[no_mangle]
fn __rust_dealloc(ptr: *mut u8, _size: usize, _align: usize) {
unsafe { bindings::kfree(ptr as *const core::ffi::c_void) };
// SAFETY: `ptr` only ever comes from `krealloc` via `__rust_alloc`
unsafe { bindings::kfree(ptr.cast()) };
}

#[no_mangle]
fn __rust_realloc(ptr: *mut u8, _old_size: usize, _align: usize, new_size: usize) -> *mut u8 {
unsafe {
bindings::krealloc(
ptr as *const core::ffi::c_void,
new_size,
bindings::GFP_KERNEL,
) as *mut u8
}
// SAFETY: `ptr` only ever comes from `krealloc` via `__rust_alloc`
unsafe { bindings::krealloc(ptr.cast(), new_size, bindings::GFP_KERNEL).cast() }
}

#[no_mangle]
fn __rust_alloc_zeroed(size: usize, _align: usize) -> *mut u8 {
// SAFETY: `krealloc` can handle zero-sized allocations with `__GFP_ZERO`
unsafe {
bindings::krealloc(
core::ptr::null(),
size,
bindings::GFP_KERNEL | bindings::__GFP_ZERO,
) as *mut u8
)
.cast()
}
}
13 changes: 8 additions & 5 deletions rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,11 @@ impl fmt::Debug for Error {
match self.name() {
// Print out number if no name can be found.
None => f.debug_tuple("Error").field(&-self.0).finish(),
// SAFETY: These strings are ASCII-only.
Some(name) => f
.debug_tuple(unsafe { core::str::from_utf8_unchecked(name) })
.debug_tuple(
// SAFETY: These strings are ASCII-only.
unsafe { core::str::from_utf8_unchecked(name) },
)
.finish(),
}
}
Expand Down Expand Up @@ -281,18 +283,19 @@ pub(crate) fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
// SAFETY: The FFI function does not deref the pointer.
if unsafe { bindings::IS_ERR(const_ptr) } {
// SAFETY: The FFI function does not deref the pointer.
let err = unsafe { bindings::PTR_ERR(const_ptr) };
let errno = unsafe { bindings::PTR_ERR(const_ptr) };
// CAST: If `IS_ERR()` returns `true`,
// then `PTR_ERR()` is guaranteed to return a
// negative value greater-or-equal to `-bindings::MAX_ERRNO`,
// which always fits in an `i16`, as per the invariant above.
// And an `i16` always fits in an `i32`. So casting `err` to
// an `i32` can never overflow, and is always valid.
//
#[allow(clippy::unnecessary_cast)]
// SAFETY: `IS_ERR()` ensures `err` is a
// negative value greater-or-equal to `-bindings::MAX_ERRNO`.
#[allow(clippy::unnecessary_cast)]
return Err(unsafe { Error::from_errno_unchecked(err as core::ffi::c_int) });
let err = unsafe { Error::from_errno_unchecked(errno as core::ffi::c_int) };
return Err(err);
}
Ok(ptr)
}
Expand Down
22 changes: 18 additions & 4 deletions rust/kernel/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ macro_rules! try_pin_init {
// no possibility of returning without `unsafe`.
struct __InitOk;
// Get the pin data from the supplied type.
// SAFETY: TODO
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: TODO
// SAFETY: we are in one of the macros permitted to call `__pin_data()` .

let data = unsafe {
use $crate::init::__internal::HasPinData;
$t$(::<$($generics),*>)?::__pin_data()
Expand Down Expand Up @@ -664,6 +665,7 @@ macro_rules! try_pin_init {
let init = move |slot| -> ::core::result::Result<(), $err> {
init(slot).map(|__InitOk| ())
};
// SAFETY: TODO
let init = unsafe { $crate::init::pin_init_from_closure::<_, $err>(init) };
init
}};
Expand Down Expand Up @@ -735,8 +737,9 @@ macro_rules! try_pin_init {
@acc($($acc:tt)*),
) => {
// Endpoint, nothing more to munch, create the initializer.
// Since we are in the `if false` branch, this will never get executed. We abuse `slot` to
// get the correct type inference here:

// SAFETY: Since we are in the `if false` branch, this will never get executed. We abuse
// `slot` to get the correct type inference here:
Comment on lines -738 to +742
Copy link
Member

Choose a reason for hiding this comment

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

I would leave the original comment and just add SAFETY: this code is never executed.

unsafe {
::core::ptr::write($slot, $t {
$($acc)*
Expand Down Expand Up @@ -777,6 +780,7 @@ macro_rules! try_pin_init {
(forget_guards:
@munch_fields($field:ident <- $val:expr, $($rest:tt)*),
) => {
// SAFETY: forgetting the guard is intentional
unsafe { $crate::init::__internal::DropGuard::forget($field) };
Comment on lines +783 to 784
Copy link
Member

Choose a reason for hiding this comment

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

You can leave these (forgetting guards) with a SAFETY: TODO, since this part will be rewritten in an update soon and then no unsafe will be needed.


$crate::try_pin_init!(forget_guards:
Expand All @@ -786,6 +790,7 @@ macro_rules! try_pin_init {
(forget_guards:
@munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
) => {
// SAFETY: forgetting the guard is intentional
unsafe { $crate::init::__internal::DropGuard::forget($field) };

$crate::try_pin_init!(forget_guards:
Expand Down Expand Up @@ -891,6 +896,7 @@ macro_rules! try_init {
// no possibility of returning without `unsafe`.
struct __InitOk;
// Get the init data from the supplied type.
// SAFETY: `HasInitData` is a marker trait which we are allowed to use in this context
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: `HasInitData` is a marker trait which we are allowed to use in this context
// SAFETY: we are in one of the macros permitted to call `__init_data()` .

let data = unsafe {
use $crate::init::__internal::HasInitData;
$t$(::<$($generics),*>)?::__init_data()
Expand Down Expand Up @@ -933,6 +939,7 @@ macro_rules! try_init {
let init = move |slot| -> ::core::result::Result<(), $err> {
init(slot).map(|__InitOk| ())
};
// SAFETY: TODO
let init = unsafe { $crate::init::init_from_closure::<_, $err>(init) };
init
}};
Expand Down Expand Up @@ -999,8 +1006,8 @@ macro_rules! try_init {
@acc($($acc:tt)*),
) => {
// Endpoint, nothing more to munch, create the initializer.
// Since we are in the `if false` branch, this will never get executed. We abuse `slot` to
// get the correct type inference here:
// SAFETY: Since we are in the `if false` branch, this will never get
// executed. We abuse `slot` to get the correct type inference here:
Comment on lines -1002 to +1010
Copy link
Member

Choose a reason for hiding this comment

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

same as above

unsafe {
::core::ptr::write($slot, $t {
$($acc)*
Expand Down Expand Up @@ -1041,6 +1048,7 @@ macro_rules! try_init {
(forget_guards:
@munch_fields($field:ident <- $val:expr, $($rest:tt)*),
) => {
// SAFETY: forgetting the guard is intentional
unsafe { $crate::init::__internal::DropGuard::forget($field) };

$crate::try_init!(forget_guards:
Expand All @@ -1050,6 +1058,7 @@ macro_rules! try_init {
(forget_guards:
@munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*),
) => {
// SAFETY: forgetting the guard is intentional
unsafe { $crate::init::__internal::DropGuard::forget($field) };

$crate::try_init!(forget_guards:
Expand Down Expand Up @@ -1197,6 +1206,7 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {
// SAFETY: Every type can be initialized by-value.
unsafe impl<T, E> Init<T, E> for T {
unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
// SAFETY: `slot` is valid per the invariants of `__init`
unsafe { slot.write(self) };
Ok(())
}
Expand Down Expand Up @@ -1371,8 +1381,12 @@ pub fn zeroed<T: Zeroable>() -> impl Init<T> {
}
}

/// # Safety
///
/// This can only be used on types that meet `Zeroable`'s guidelines
Comment on lines +1384 to +1386
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// # Safety
///
/// This can only be used on types that meet `Zeroable`'s guidelines
/// # Safety
///
/// This can only be used on types that meet `Zeroable`'s safety contract.

macro_rules! impl_zeroable {
($($({$($generics:tt)*})? $t:ty, )*) => {
// SAFETY: upheld by documented use
$(unsafe impl$($($generics)*)? Zeroable for $t {})*
};
}
Expand Down
2 changes: 2 additions & 0 deletions rust/kernel/init/__internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
//! - `macros/pin_data.rs`
//! - `macros/pinned_drop.rs`

#![allow(clippy::undocumented_unsafe_blocks)]
Copy link
Member

Choose a reason for hiding this comment

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

I think we should also document these here.

Copy link
Collaborator Author

@tgross35 tgross35 Jul 16, 2023

Choose a reason for hiding this comment

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

It's only misssing two:

unsafe impl<T: ?Sized> InitData for AllData<T>

and

unsafe impl<T: ?Sized> HasInitData for T 

Would you mind providing the comments for them?


use super::*;

/// See the [nomicon] for what subtyping is. See also [this table].
Expand Down
3 changes: 3 additions & 0 deletions rust/kernel/init/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ macro_rules! __pin_data {
}
}

// SAFETY: `__ThePinData` has the correct projections
unsafe impl<$($impl_generics)*>
$crate::init::__internal::PinData for __ThePinData<$($ty_generics)*>
where $($whr)*
Expand Down Expand Up @@ -965,6 +966,7 @@ macro_rules! __pin_data {
slot: *mut $p_type,
init: impl $crate::init::PinInit<$p_type, E>,
) -> ::core::result::Result<(), E> {
// SAFETY: `slot` is valid per this function's contract
unsafe { $crate::init::PinInit::__pinned_init(init, slot) }
}
)*
Expand All @@ -974,6 +976,7 @@ macro_rules! __pin_data {
slot: *mut $type,
init: impl $crate::init::Init<$type, E>,
) -> ::core::result::Result<(), E> {
// SAFETY: `slot` is valid per this function's contract
Copy link
Member

Choose a reason for hiding this comment

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

The function has no # Safety section, so it effectively has no contract. I think we should just give it the same contract as Init::__init.

unsafe { $crate::init::Init::__init(init, slot) }
}
)*
Expand Down
27 changes: 17 additions & 10 deletions rust/kernel/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@
use core::{
ffi::{c_char, c_void},
fmt,
ptr::addr_of,
};

use crate::str::RawFormatter;

#[cfg(CONFIG_PRINTK)]
use crate::bindings;

// Called from `vsprintf` with format specifier `%pA`.
/// Called from `vsprintf` with format specifier `%pA`.
///
/// # Safety
///
/// - Memory within `buf..end` must be valid for write
/// - `ptr` must point to a valid instance of `core::fmt::Arguments`
#[no_mangle]
unsafe extern "C" fn rust_fmt_argument(
buf: *mut c_char,
Expand All @@ -26,7 +32,8 @@ unsafe extern "C" fn rust_fmt_argument(
use fmt::Write;
// SAFETY: The C contract guarantees that `buf` is valid if it's less than `end`.
let mut w = unsafe { RawFormatter::from_ptrs(buf.cast(), end.cast()) };
let _ = w.write_fmt(unsafe { *(ptr as *const fmt::Arguments<'_>) });
// SAFETY: Upheld by this function's safety contract
let _ = w.write_fmt(unsafe { *(ptr.cast::<fmt::Arguments<'_>>()) });
w.pos().cast()
}

Expand Down Expand Up @@ -105,13 +112,14 @@ pub unsafe fn call_printk(
module_name: &[u8],
args: fmt::Arguments<'_>,
) {
// `_printk` does not seem to fail in any path.
#[cfg(CONFIG_PRINTK)]
// `_printk` does not seem to fail in any path.
// SAFETY: This function's invariants make this call safe.
unsafe {
bindings::_printk(
format_string.as_ptr() as _,
format_string.as_ptr().cast(),
module_name.as_ptr(),
&args as *const _ as *const c_void,
addr_of!(args).cast::<c_void>(),
);
}
}
Expand All @@ -124,14 +132,13 @@ pub unsafe fn call_printk(
#[doc(hidden)]
#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
pub fn call_printk_cont(args: fmt::Arguments<'_>) {
// `_printk` does not seem to fail in any path.
//
// SAFETY: The format string is fixed.
#[cfg(CONFIG_PRINTK)]
// `_printk` does not seem to fail in any path.
// SAFETY: `CONT` and `args` meet `_printk`'s safety contract
unsafe {
bindings::_printk(
format_strings::CONT.as_ptr() as _,
&args as *const _ as *const c_void,
format_strings::CONT.as_ptr().cast(),
addr_of!(args).cast::<c_void>(),
);
}
}
Expand Down
7 changes: 4 additions & 3 deletions rust/kernel/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ impl CStr {
/// Returns the length of this string with `NUL`.
#[inline]
pub const fn len_with_nul(&self) -> usize {
// SAFETY: This is one of the invariant of `CStr`.
// We add a `unreachable_unchecked` here to hint the optimizer that
// the value returned from this function is non-zero.
if self.0.is_empty() {
// SAFETY: This is one of the invariant of `CStr`.
// We add a `unreachable_unchecked` here to hint the optimizer that
// the value returned from this function is non-zero.
unsafe { core::hint::unreachable_unchecked() };
}
self.0.len()
Expand Down Expand Up @@ -198,6 +198,7 @@ impl CStr {
/// ```
#[inline]
pub unsafe fn as_str_unchecked(&self) -> &str {
// SAFETY: invariant is upheld by this function's safety contract
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
}

Expand Down
2 changes: 1 addition & 1 deletion rust/kernel/sync/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ pub struct CondVar {
_pin: PhantomPinned,
}

// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on any thread.
#[allow(clippy::non_send_fields_in_send_ty)]
// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on any thread.
unsafe impl Send for CondVar {}

// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on multiple threads
Expand Down
6 changes: 3 additions & 3 deletions rust/kernel/sync/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ impl<T: ?Sized, B: Backend> Guard<'_, T, B> {
// SAFETY: The caller owns the lock, so it is safe to unlock it.
unsafe { B::unlock(self.lock.state.get(), &self.state) };

// SAFETY: The lock was just unlocked above and is being relocked now.
let _relock =
ScopeGuard::new(|| unsafe { B::relock(self.lock.state.get(), &mut self.state) });
let _relock = ScopeGuard::new(||
// SAFETY: The lock was just unlocked above and is being relocked now.
unsafe { B::relock(self.lock.state.get(), &mut self.state) });

cb();
}
Expand Down
4 changes: 4 additions & 0 deletions rust/rust_common_flags
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,12 @@
-Dclippy::same_functions_in_if_condition
-Dclippy::stable_sort_primitive
-Dclippy::too_many_lines
-Dclippy::undocumented_unsafe_blocks
-Dclippy::unicode_not_nfc
-Dclippy::unnecessary_join
# FIXME: enable this at the next version bump. Disabled because of false
# positive in macros: https://github.com/rust-lang/rust-clippy/issues/10084
# -Dclippy::unnecessary_safety_comment
Comment on lines +85 to +87
Copy link
Member

Choose a reason for hiding this comment

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

I would explicitly mention the version it has started working instead; or otherwise not mention the FIXME part if we don't know.

(We are upgrading to 1.70.0/1.71.0 soon)

Also, this should probably be in another commit.

-Dclippy::unnested_or_patterns

# Clippy lints from restriction
Expand Down
1 change: 1 addition & 0 deletions rust/uapi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))]
#![allow(
clippy::all,
clippy::undocumented_unsafe_blocks,
missing_docs,
non_camel_case_types,
non_upper_case_globals,
Expand Down