Skip to content

Commit 62d3b58

Browse files
author
Sven Van Asbroeck
committed
rust/error: add helper function to convert a C error pointer to a Result
Some kernel C API functions return a pointer which embeds an optional `errno`. Callers are supposed to check the returned pointer with `IS_ERR()` and if this returns `true`, retrieve the `errno` using `PTR_ERR()`. Create a Rust helper function to implement the Rust equivalent: transform a `*mut T` to `Result<*mut T>`. Co-Created-By: Boqun Feng <[email protected]> Co-Created-By: Miguel Ojeda <[email protected]> Signed-off-by: Sven Van Asbroeck <[email protected]>
1 parent fc2b177 commit 62d3b58

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

rust/helpers.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ size_t rust_helper_copy_to_iter(const void *addr, size_t bytes, struct iov_iter
105105
}
106106
EXPORT_SYMBOL_GPL(rust_helper_copy_to_iter);
107107

108+
bool rust_helper_is_err(__force const void *ptr)
109+
{
110+
return IS_ERR(ptr);
111+
}
112+
EXPORT_SYMBOL_GPL(rust_helper_is_err);
113+
114+
long rust_helper_ptr_err(__force const void *ptr)
115+
{
116+
return PTR_ERR(ptr);
117+
}
118+
EXPORT_SYMBOL_GPL(rust_helper_ptr_err);
119+
108120
#if !defined(CONFIG_ARM)
109121
// See https://github.com/rust-lang/rust-bindgen/issues/1671
110122
static_assert(__builtin_types_compatible_p(size_t, uintptr_t),

rust/kernel/error.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,57 @@ impl From<AllocError> for Error {
104104
Error::ENOMEM
105105
}
106106
}
107+
108+
// # Invariant: `-bindings::MAX_ERRNO` fits in an `i16`.
109+
crate::static_assert!(bindings::MAX_ERRNO as i64 <= -(i16::MIN as i64));
110+
111+
/// Transform a kernel "error pointer" to a normal pointer.
112+
///
113+
/// Some kernel C API functions return an "error pointer" which optionally
114+
/// embeds an `errno`. Callers are supposed to check the returned pointer
115+
/// for errors. This function performs the check and converts the "error pointer"
116+
/// to a normal pointer in an idiomatic fashion.
117+
///
118+
/// # Examples
119+
///
120+
/// ```rust,no_run
121+
/// fn devm_platform_ioremap_resource(
122+
/// pdev: &mut PlatformDevice,
123+
/// index: u32,
124+
/// ) -> Result<*mut c_types::c_void> {
125+
/// // SAFETY: FFI call.
126+
/// unsafe {
127+
/// from_kernel_err_ptr(bindings::devm_platform_ioremap_resource(
128+
/// pdev.to_ptr(),
129+
/// index,
130+
/// ))
131+
/// }
132+
/// }
133+
/// ```
134+
// TODO: remove `dead_code` marker once an in-kernel client is available.
135+
#[allow(dead_code)]
136+
pub(crate) fn from_kernel_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
137+
extern "C" {
138+
#[allow(improper_ctypes)]
139+
fn rust_helper_is_err(ptr: *const c_types::c_void) -> bool;
140+
141+
#[allow(improper_ctypes)]
142+
fn rust_helper_ptr_err(ptr: *const c_types::c_void) -> c_types::c_long;
143+
}
144+
145+
// CAST: casting a pointer to `*const c_types::c_void` is always valid.
146+
let const_ptr: *const c_types::c_void = ptr.cast();
147+
// SAFETY: the FFI function does not deref the pointer.
148+
if unsafe { rust_helper_is_err(const_ptr) } {
149+
// SAFETY: the FFI function does not deref the pointer.
150+
let err = unsafe { rust_helper_ptr_err(const_ptr) };
151+
// CAST: if `rust_helper_is_err()` returns `true`,
152+
// then `rust_helper_ptr_err()` is guaranteed to return a
153+
// negative value greater-or-equal to `-bindings::MAX_ERRNO`,
154+
// which always fits in an `i16`, as per the invariant above.
155+
// And an `i16` always fits in an `i32`. So casting `err` to
156+
// an `i32` can never overflow, and is always valid.
157+
return Err(Error::from_kernel_errno(err as i32));
158+
}
159+
Ok(ptr)
160+
}

0 commit comments

Comments
 (0)