Skip to content

Commit d032957

Browse files
committed
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 <[email protected]>
1 parent ac536cc commit d032957

File tree

1 file changed

+34
-2
lines changed

1 file changed

+34
-2
lines changed

rust/kernel/error.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ use core::str::{self, Utf8Error};
1616
///
1717
/// The kernel defines a set of integer generic error codes based on C and
1818
/// POSIX ones. These codes may have a more specific meaning in some contexts.
19+
///
20+
/// # Invariants
21+
///
22+
/// The value is a valid `errno` (i.e. `>= -MAX_ERRNO && < 0`).
1923
#[derive(Clone, Copy, PartialEq, Eq)]
2024
pub struct Error(c_types::c_int);
2125

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

5963
/// Creates an [`Error`] from a kernel error code.
60-
pub fn from_kernel_errno(errno: c_types::c_int) -> Error {
64+
///
65+
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would
66+
/// be returned in such a case.
67+
pub(crate) fn from_kernel_errno(errno: c_types::c_int) -> Error {
68+
if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 {
69+
// TODO: make it a `WARN_ONCE` once available.
70+
crate::pr_warn!(
71+
"attempted to create `Error` with out of range `errno`: {}",
72+
errno
73+
);
74+
return Error::EINVAL;
75+
}
76+
77+
// INVARIANT: the check above ensures the type invariant
78+
// will hold.
79+
Error(errno)
80+
}
81+
82+
/// Creates an [`Error`] from a kernel error code.
83+
///
84+
/// # Safety
85+
///
86+
/// `errno` must be within error code range (i.e. `>= -MAX_ERRNO && < 0`).
87+
pub(crate) unsafe fn from_kernel_errno_unchecked(errno: c_types::c_int) -> Error {
88+
// INVARIANT: the contract ensures the type invariant
89+
// will hold.
6190
Error(errno)
6291
}
6392

@@ -227,7 +256,10 @@ pub(crate) fn from_kernel_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
227256
// which always fits in an `i16`, as per the invariant above.
228257
// And an `i16` always fits in an `i32`. So casting `err` to
229258
// an `i32` can never overflow, and is always valid.
230-
return Err(Error::from_kernel_errno(err as i32));
259+
//
260+
// SAFETY: `rust_helper_is_err()` ensures `err` is a
261+
// negative value greater-or-equal to `-bindings::MAX_ERRNO`
262+
return Err(unsafe { Error::from_kernel_errno_unchecked(err as i32) });
231263
}
232264
Ok(ptr)
233265
}

0 commit comments

Comments
 (0)