Skip to content

Commit af5826e

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 af5826e

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-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: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,62 @@ 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+
/// # Safety
119+
///
120+
/// `ptr` must be valid or null.
121+
///
122+
/// # Examples
123+
///
124+
/// ```rust,no_run
125+
/// fn devm_platform_ioremap_resource(
126+
/// pdev: &mut PlatformDevice,
127+
/// index: u32,
128+
/// ) -> Result<*mut c_types::c_void> {
129+
/// // SAFETY: FFI call.
130+
/// unsafe {
131+
/// ptr_err_check(bindings::devm_platform_ioremap_resource(
132+
/// pdev.to_ptr(),
133+
/// index,
134+
/// ))
135+
/// }
136+
/// }
137+
/// ```
138+
// TODO: remove `dead_code` marker once an in-kernel client is available.
139+
#[allow(dead_code)]
140+
pub(crate) fn from_kernel_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
141+
extern "C" {
142+
#[allow(improper_ctypes)]
143+
fn rust_helper_is_err(ptr: *const c_types::c_void) -> bool;
144+
145+
#[allow(improper_ctypes)]
146+
fn rust_helper_ptr_err(ptr: *const c_types::c_void) -> c_types::c_long;
147+
}
148+
149+
// SAFETY: any pointer can be safely cast to a `*const c_types::c_void`.
150+
let const_ptr = ptr as *const c_types::c_void;
151+
// SAFETY: `IS_ERR()` requires a valid-or-null pointer, and `const_ptr`
152+
// is valid-or-null as per Safety above. So it is safe to call.
153+
if unsafe { rust_helper_is_err(const_ptr) } {
154+
// SAFETY: `PTR_ERR()` equires a valid-or-null pointer, and `const_ptr`
155+
// is valid-or-null as per Safety above. So it is safe to call.
156+
let err = unsafe { rust_helper_ptr_err(const_ptr) };
157+
// NO-OVERFLOW: if `rust_helper_is_err()` returns `true`,
158+
// then `rust_helper_ptr_err()` is guaranteed to return a
159+
// negative value greater-or-equal to `-bindings::MAX_ERRNO`,
160+
// which always fits in an `i16`, as per the invariant above.
161+
// And an `i16` always fits in an `i32`. So the cast is safe.
162+
return Err(Error::from_kernel_errno(err as i32));
163+
}
164+
Ok(ptr)
165+
}

0 commit comments

Comments
 (0)