Skip to content

Commit eb4fba6

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 fb672e0 commit eb4fba6

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-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: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,54 @@ macro_rules! from_kernel_result {
152152
})())
153153
}};
154154
}
155+
156+
/// Transform a kernel "error pointer" to a normal pointer.
157+
///
158+
/// Some kernel C API functions return an "error pointer" which optionally
159+
/// embeds an `errno`. Callers are supposed to check the returned pointer
160+
/// for errors. This function performs the check and converts the "error pointer"
161+
/// to a normal pointer in an idiomatic fashion.
162+
///
163+
/// # Examples
164+
///
165+
/// ```rust,no_run
166+
/// fn devm_platform_ioremap_resource(
167+
/// pdev: &mut PlatformDevice,
168+
/// index: u32,
169+
/// ) -> Result<*mut c_types::c_void> {
170+
/// // SAFETY: FFI call.
171+
/// unsafe {
172+
/// from_kernel_err_ptr(bindings::devm_platform_ioremap_resource(
173+
/// pdev.to_ptr(),
174+
/// index,
175+
/// ))
176+
/// }
177+
/// }
178+
/// ```
179+
// TODO: remove `dead_code` marker once an in-kernel client is available.
180+
#[allow(dead_code)]
181+
pub(crate) fn from_kernel_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
182+
extern "C" {
183+
#[allow(improper_ctypes)]
184+
fn rust_helper_is_err(ptr: *const c_types::c_void) -> bool;
185+
186+
#[allow(improper_ctypes)]
187+
fn rust_helper_ptr_err(ptr: *const c_types::c_void) -> c_types::c_long;
188+
}
189+
190+
// CAST: casting a pointer to `*const c_types::c_void` is always valid.
191+
let const_ptr: *const c_types::c_void = ptr.cast();
192+
// SAFETY: the FFI function does not deref the pointer.
193+
if unsafe { rust_helper_is_err(const_ptr) } {
194+
// SAFETY: the FFI function does not deref the pointer.
195+
let err = unsafe { rust_helper_ptr_err(const_ptr) };
196+
// CAST: if `rust_helper_is_err()` returns `true`,
197+
// then `rust_helper_ptr_err()` is guaranteed to return a
198+
// negative value greater-or-equal to `-bindings::MAX_ERRNO`,
199+
// which always fits in an `i16`, as per the invariant above.
200+
// And an `i16` always fits in an `i32`. So casting `err` to
201+
// an `i32` can never overflow, and is always valid.
202+
return Err(Error::from_kernel_errno(err as i32));
203+
}
204+
Ok(ptr)
205+
}

0 commit comments

Comments
 (0)