Skip to content

Commit 5c65d82

Browse files
authored
Merge pull request #324 from foxhlchen/295-ERROR-invariant
rust: check range and add type invariant to Error (issue #295)
2 parents ac536cc + d032957 commit 5c65d82

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)