From d0329579a9c13339dd6e7d2cb951de2b86ebc9c9 Mon Sep 17 00:00:00 2001 From: Fox Chen Date: Mon, 31 May 2021 14:44:33 +0800 Subject: [PATCH] Rust: check range and add type invariant to Error 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 --- rust/kernel/error.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index e22ba85e2250de..6ac8630c8fb203 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -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); @@ -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) } @@ -227,7 +256,10 @@ pub(crate) fn from_kernel_err_ptr(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) }