Skip to content

Commit

Permalink
Merge pull request torvalds#324 from foxhlchen/295-ERROR-invariant
Browse files Browse the repository at this point in the history
rust: check range and add type invariant to Error (issue torvalds#295)
  • Loading branch information
ojeda authored Jun 3, 2021
2 parents ac536cc + d032957 commit 5c65d82
Showing 1 changed file with 34 additions and 2 deletions.
36 changes: 34 additions & 2 deletions rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ use core::str::{self, Utf8Error};
///
/// The kernel defines a set of integer generic error codes based on C and
/// POSIX ones. These codes may have a more specific meaning in some contexts.
///
/// # Invariants
///
/// The value is a valid `errno` (i.e. `>= -MAX_ERRNO && < 0`).
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Error(c_types::c_int);

Expand Down Expand Up @@ -57,7 +61,32 @@ impl Error {
pub const EBADF: Self = Error(-(bindings::EBADF as i32));

/// Creates an [`Error`] from a kernel error code.
pub fn from_kernel_errno(errno: c_types::c_int) -> Error {
///
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would
/// be returned in such a case.
pub(crate) fn from_kernel_errno(errno: c_types::c_int) -> Error {
if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 {
// TODO: make it a `WARN_ONCE` once available.
crate::pr_warn!(
"attempted to create `Error` with out of range `errno`: {}",
errno
);
return Error::EINVAL;
}

// INVARIANT: the check above ensures the type invariant
// will hold.
Error(errno)
}

/// Creates an [`Error`] from a kernel error code.
///
/// # Safety
///
/// `errno` must be within error code range (i.e. `>= -MAX_ERRNO && < 0`).
pub(crate) unsafe fn from_kernel_errno_unchecked(errno: c_types::c_int) -> Error {
// INVARIANT: the contract ensures the type invariant
// will hold.
Error(errno)
}

Expand Down Expand Up @@ -227,7 +256,10 @@ pub(crate) fn from_kernel_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
// 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.
return Err(Error::from_kernel_errno(err as i32));
//
// SAFETY: `rust_helper_is_err()` ensures `err` is a
// negative value greater-or-equal to `-bindings::MAX_ERRNO`
return Err(unsafe { Error::from_kernel_errno_unchecked(err as i32) });
}
Ok(ptr)
}

0 comments on commit 5c65d82

Please sign in to comment.