Skip to content

Commit

Permalink
Rust: check range and add type invariant to Error
Browse files Browse the repository at this point in the history
We will need to make sure that no Error with out of
range error code can be constructed.

This commit
1. Add errno check in from_kernel_errno()
2. Provides a unchecked version from_kernel_errno_unchecked()

And when an invalid errno is found, it will
1) Print a  warning.
2) Convert it to EINVAL.

Signed-off-by: Fox Chen <[email protected]>
  • Loading branch information
foxhlchen committed Jun 3, 2021
1 parent ac536cc commit d032957
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 d032957

Please sign in to comment.