| 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | |
| 3 | //! Error propagation for QEMU Rust code |
| 4 | //! |
| 5 | //! This module contains [`Error`], the bridge between Rust errors and |
| 6 | //! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) |
| 7 | //! struct. |
| 8 | //! |
| 9 | //! For FFI code, [`Error`] provides functions to simplify conversion between |
| 10 | //! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: |
| 11 | //! |
| 12 | //! * [`ok_or_propagate`](crate::Error::ok_or_propagate), |
| 13 | //! [`bool_or_propagate`](crate::Error::bool_or_propagate), |
| 14 | //! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build |
| 15 | //! a C return value while also propagating an error condition |
| 16 | //! |
| 17 | //! * [`with_errp`](crate::Error::with_errp) can be used to build a `Result` |
| 18 | //! |
| 19 | //! This module is most commonly used at the boundary between C and Rust code; |
| 20 | //! other code will usually access it through the |
| 21 | //! [`utils::Result`](crate::Result) type alias, and will use the |
| 22 | //! [`std::error::Error`] interface to let C errors participate in Rust's error |
| 23 | //! handling functionality. |
| 24 | //! |
| 25 | //! Rust code can also create use this module to create an error object that |
| 26 | //! will be passed up to C code, though in most cases this will be done |
| 27 | //! transparently through the `?` operator. Errors can be constructed from a |
| 28 | //! simple error string, from an [`anyhow::Error`] to pass any other Rust error |
| 29 | //! type up to C code, or from a combination of the two. |
| 30 | //! |
| 31 | //! The third case, corresponding to [`Error::with_error`], is the only one that |
| 32 | //! requires mentioning [`utils::Error`](crate::Error) explicitly. Similar |
| 33 | //! to how QEMU's C code handles errno values, the string and the |
| 34 | //! `anyhow::Error` object will be concatenated with `:` as the separator. |
| 35 | |
| 36 | use std::{ |
| 37 | borrow::Cow, |
| 38 | ffi::{c_char, c_int, c_void, CStr}, |
| 39 | fmt::{self, Display}, |
| 40 | ops::Deref, |
| 41 | panic, |
| 42 | ptr::{self, addr_of_mut}, |
| 43 | }; |
| 44 | |
| 45 | use foreign::{prelude::*, OwnedPointer}; |
| 46 | |
| 47 | use crate::bindings; |
| 48 | |
| 49 | pub type Result<T> = std::result::Result<T, Error>; |
| 50 | |
| 51 | #[derive(Debug)] |
| 52 | pub struct Error { |
| 53 | cause: anyhow::Error, |
| 54 | file: &'static str, |
| 55 | line: u32, |
| 56 | } |
| 57 | |
| 58 | impl Deref for Error { |
| 59 | type Target = anyhow::Error; |
| 60 | |
| 61 | fn deref(&self) -> &Self::Target { |
| 62 | &self.cause |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | impl Display for Error { |
| 67 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 68 | Display::fmt(&format_args!("{:#}", self.cause), f) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | impl<E> From<E> for Error |
| 73 | where |
| 74 | anyhow::Error: From<E>, |
| 75 | { |
| 76 | #[track_caller] |
| 77 | fn from(src: E) -> Self { |
| 78 | Self::new(anyhow::Error::from(src)) |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | impl Error { |
| 83 | /// Create a new error from an [`anyhow::Error`]. |
| 84 | /// |
| 85 | /// This wraps the error with QEMU's location tracking information. |
| 86 | /// Most code should use the `?` operator instead of calling this directly. |
| 87 | #[track_caller] |
| 88 | pub fn new(cause: anyhow::Error) -> Self { |
| 89 | let location = panic::Location::caller(); |
| 90 | Self { |
| 91 | cause, |
| 92 | file: location.file(), |
| 93 | line: location.line(), |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | /// Create a new error from a string message. |
| 98 | /// |
| 99 | /// This is a convenience wrapper around [`Error::new`] for simple string |
| 100 | /// errors. Most code should use the [`ensure!`](crate::ensure) macro |
| 101 | /// instead of calling this directly. |
| 102 | #[track_caller] |
| 103 | pub fn msg(src: impl Into<Cow<'static, str>>) -> Self { |
| 104 | Self::new(anyhow::Error::msg(src.into())) |
| 105 | } |
| 106 | |
| 107 | #[track_caller] |
| 108 | #[doc(hidden)] |
| 109 | #[inline(always)] |
| 110 | pub fn format(args: fmt::Arguments) -> Self { |
| 111 | // anyhow::Error::msg will allocate anyway, might as well let fmt::format doit. |
| 112 | let msg = fmt::format(args); |
| 113 | Self::new(anyhow::Error::msg(msg)) |
| 114 | } |
| 115 | |
| 116 | /// Create a new error, prepending `msg` to the |
| 117 | /// description of `cause` |
| 118 | #[track_caller] |
| 119 | pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self { |
| 120 | fn do_with_error( |
| 121 | msg: Cow<'static, str>, |
| 122 | cause: anyhow::Error, |
| 123 | location: &'static panic::Location<'static>, |
| 124 | ) -> Error { |
| 125 | Error { |
| 126 | cause: cause.context(msg), |
| 127 | file: location.file(), |
| 128 | line: location.line(), |
| 129 | } |
| 130 | } |
| 131 | do_with_error(msg.into(), cause.into(), panic::Location::caller()) |
| 132 | } |
| 133 | |
| 134 | /// Consume a result, returning `false` if it is an error and |
| 135 | /// `true` if it is successful. The error is propagated into |
| 136 | /// `errp` like the C API `error_propagate` would do. |
| 137 | /// |
| 138 | /// # Safety |
| 139 | /// |
| 140 | /// `errp` must be a valid argument to `error_propagate`; |
| 141 | /// typically it is received from C code and need not be |
| 142 | /// checked further at the Rust↔C boundary. |
| 143 | pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { |
| 144 | // SAFETY: caller guarantees errp is valid |
| 145 | unsafe { Self::ok_or_propagate(result, errp) }.is_some() |
| 146 | } |
| 147 | |
| 148 | /// Consume a result, returning a `NULL` pointer if it is an error and |
| 149 | /// a C representation of the contents if it is successful. This is |
| 150 | /// similar to the C API `error_propagate`, but it panics if `*errp` |
| 151 | /// is not `NULL`. |
| 152 | /// |
| 153 | /// # Safety |
| 154 | /// |
| 155 | /// `errp` must be a valid argument to `error_propagate`; |
| 156 | /// typically it is received from C code and need not be |
| 157 | /// checked further at the Rust↔C boundary. |
| 158 | /// |
| 159 | /// See [`propagate`](Error::propagate) for more information. |
| 160 | #[must_use] |
| 161 | pub unsafe fn ptr_or_propagate<T: CloneToForeign>( |
| 162 | result: Result<T>, |
| 163 | errp: *mut *mut bindings::Error, |
| 164 | ) -> *mut T::Foreign { |
| 165 | // SAFETY: caller guarantees errp is valid |
| 166 | unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() |
| 167 | } |
| 168 | |
| 169 | /// Consume a result in the same way as `self.ok()`, but also propagate |
| 170 | /// a possible error into `errp`. This is similar to the C API |
| 171 | /// `error_propagate`, but it panics if `*errp` is not `NULL`. |
| 172 | /// |
| 173 | /// # Safety |
| 174 | /// |
| 175 | /// `errp` must be a valid argument to `error_propagate`; |
| 176 | /// typically it is received from C code and need not be |
| 177 | /// checked further at the Rust↔C boundary. |
| 178 | /// |
| 179 | /// See [`propagate`](Error::propagate) for more information. |
| 180 | pub unsafe fn ok_or_propagate<T>( |
| 181 | result: Result<T>, |
| 182 | errp: *mut *mut bindings::Error, |
| 183 | ) -> Option<T> { |
| 184 | result.map_err(|err| unsafe { err.propagate(errp) }).ok() |
| 185 | } |
| 186 | |
| 187 | /// Equivalent of the C function `error_propagate`. Fill `*errp` |
| 188 | /// with the information container in `self` if `errp` is not NULL; |
| 189 | /// then consume it. |
| 190 | /// |
| 191 | /// This is similar to the C API `error_propagate`, but it panics if |
| 192 | /// `*errp` is not `NULL`. |
| 193 | /// |
| 194 | /// # Safety |
| 195 | /// |
| 196 | /// `errp` must be a valid argument to `error_propagate`; it can be |
| 197 | /// `NULL` or it can point to any of: |
| 198 | /// * `error_abort` |
| 199 | /// * `error_fatal` |
| 200 | /// * a local variable of (C) type `Error *` |
| 201 | /// |
| 202 | /// Typically `errp` is received from C code and need not be |
| 203 | /// checked further at the Rust↔C boundary. |
| 204 | pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { |
| 205 | if errp.is_null() { |
| 206 | return; |
| 207 | } |
| 208 | |
| 209 | // SAFETY: caller guarantees that errp and *errp are valid |
| 210 | unsafe { |
| 211 | assert_eq!(*errp, ptr::null_mut()); |
| 212 | bindings::error_propagate(errp, self.clone_to_foreign_ptr()); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | /// Pass a C `Error*` to the closure, and convert the result |
| 217 | /// (either the return value of the closure, or the error) |
| 218 | /// into a Rust `Result`. |
| 219 | /// |
| 220 | /// # Safety |
| 221 | /// |
| 222 | /// One exit from `f`, `c_error` must be unchanged or point to a |
| 223 | /// valid C [`struct Error`](bindings::Error). |
| 224 | pub unsafe fn with_errp<T, F: FnOnce(&mut *mut bindings::Error) -> T>(f: F) -> Result<T> { |
| 225 | let mut c_error: *mut bindings::Error = ptr::null_mut(); |
| 226 | |
| 227 | // SAFETY: guaranteed by the postcondition of `f` |
| 228 | match (f(&mut c_error), unsafe { c_error.into_native() }) { |
| 229 | (result, None) => Ok(result), |
| 230 | (_, Some(err)) => Err(err), |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | /// Extension trait for `std::result::Result`, providing extra |
| 236 | /// methods when the error type can be converted into a QEMU |
| 237 | /// Error. |
| 238 | pub trait ResultExt { |
| 239 | /// The success type `T` in `Result<T, E>`. |
| 240 | type OkType; |
| 241 | |
| 242 | /// Report a fatal error and exit QEMU, or return the success value. |
| 243 | /// Note that, unlike [`unwrap()`](std::result::Result::unwrap), this |
| 244 | /// is not an abort and can be used for user errors. |
| 245 | fn unwrap_fatal(self) -> Self::OkType; |
| 246 | } |
| 247 | |
| 248 | impl<T, E> ResultExt for std::result::Result<T, E> |
| 249 | where |
| 250 | Error: From<E>, |
| 251 | { |
| 252 | type OkType = T; |
| 253 | |
| 254 | fn unwrap_fatal(self) -> T { |
| 255 | // SAFETY: errp is valid |
| 256 | self.map_err(|err| unsafe { |
| 257 | Error::from(err).propagate(addr_of_mut!(bindings::error_fatal)) |
| 258 | }) |
| 259 | .unwrap() |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | impl FreeForeign for Error { |
| 264 | type Foreign = bindings::Error; |
| 265 | |
| 266 | unsafe fn free_foreign(p: *mut bindings::Error) { |
| 267 | // SAFETY: caller guarantees p is valid |
| 268 | unsafe { |
| 269 | bindings::error_free(p); |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | impl CloneToForeign for Error { |
| 275 | fn clone_to_foreign(&self) -> OwnedPointer<Self> { |
| 276 | // SAFETY: all arguments are controlled by this function |
| 277 | unsafe { |
| 278 | let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); |
| 279 | let err: &mut bindings::Error = &mut *err.cast(); |
| 280 | *err = bindings::Error { |
| 281 | msg: format!("{self}").clone_to_foreign_ptr(), |
| 282 | err_class: bindings::ERROR_CLASS_GENERIC_ERROR, |
| 283 | src_len: self.file.len() as c_int, |
| 284 | src: self.file.as_ptr().cast::<c_char>(), |
| 285 | line: self.line as c_int, |
| 286 | func: ptr::null_mut(), |
| 287 | hint: ptr::null_mut(), |
| 288 | }; |
| 289 | OwnedPointer::new(err) |
| 290 | } |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | impl FromForeign for Error { |
| 295 | unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { |
| 296 | // SAFETY: caller guarantees c_error is valid |
| 297 | unsafe { |
| 298 | let error = &*c_error; |
| 299 | let file = if error.src_len < 0 { |
| 300 | // NUL-terminated |
| 301 | CStr::from_ptr(error.src).to_str() |
| 302 | } else { |
| 303 | // Can become str::from_utf8 with Rust 1.87.0 |
| 304 | std::str::from_utf8(std::slice::from_raw_parts( |
| 305 | &*error.src.cast::<u8>(), |
| 306 | error.src_len as usize, |
| 307 | )) |
| 308 | }; |
| 309 | |
| 310 | Error { |
| 311 | cause: anyhow::Error::msg(String::cloned_from_foreign(error.msg)), |
| 312 | file: file.unwrap(), |
| 313 | line: error.line as u32, |
| 314 | } |
| 315 | } |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | /// Ensure that a condition is true, returning an error if it is false. |
| 320 | /// |
| 321 | /// This macro is similar to [`anyhow::ensure`] but returns a QEMU [`Result`]. |
| 322 | /// If the condition evaluates to `false`, the macro returns early with an error |
| 323 | /// constructed from the provided message. |
| 324 | /// |
| 325 | /// # Examples |
| 326 | /// |
| 327 | /// ``` |
| 328 | /// # use util::{ensure, Result}; |
| 329 | /// # fn check_positive(x: i32) -> Result<()> { |
| 330 | /// ensure!(x > 0, "value must be positive"); |
| 331 | /// # Ok(()) |
| 332 | /// # } |
| 333 | /// ``` |
| 334 | /// |
| 335 | /// ``` |
| 336 | /// # use util::{ensure, Result}; |
| 337 | /// # const MIN: i32 = 123; |
| 338 | /// # const MAX: i32 = 456; |
| 339 | /// # fn check_range(x: i32) -> Result<()> { |
| 340 | /// ensure!( |
| 341 | /// x >= MIN && x <= MAX, |
| 342 | /// "{} not between {} and {}", |
| 343 | /// x, |
| 344 | /// MIN, |
| 345 | /// MAX |
| 346 | /// ); |
| 347 | /// # Ok(()) |
| 348 | /// # } |
| 349 | /// ``` |
| 350 | #[macro_export] |
| 351 | macro_rules! ensure { |
| 352 | ($cond:expr, $fmt:literal, $($arg:tt)*) => { |
| 353 | if !$cond { |
| 354 | let e = $crate::Error::format(format_args!($fmt, $($arg)*)); |
| 355 | return $crate::Result::Err(e); |
| 356 | } |
| 357 | }; |
| 358 | ($cond:expr, $err:expr $(,)?) => { |
| 359 | if !$cond { |
| 360 | let e = $crate::Error::msg($err); |
| 361 | return $crate::Result::Err(e); |
| 362 | } |
| 363 | }; |
| 364 | } |
| 365 | |
| 366 | #[cfg(test)] |
| 367 | mod tests { |
| 368 | use std::ffi::CStr; |
| 369 | |
| 370 | use anyhow::anyhow; |
| 371 | use common::assert_match; |
| 372 | use foreign::OwnedPointer; |
| 373 | |
| 374 | use super::*; |
| 375 | |
| 376 | #[track_caller] |
| 377 | fn error_for_test(msg: &CStr) -> OwnedPointer<Error> { |
| 378 | // SAFETY: all arguments are controlled by this function |
| 379 | let location = panic::Location::caller(); |
| 380 | unsafe { |
| 381 | let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); |
| 382 | let err: &mut bindings::Error = &mut *err.cast(); |
| 383 | *err = bindings::Error { |
| 384 | msg: msg.clone_to_foreign_ptr(), |
| 385 | err_class: bindings::ERROR_CLASS_GENERIC_ERROR, |
| 386 | src_len: location.file().len() as c_int, |
| 387 | src: location.file().as_ptr().cast::<c_char>(), |
| 388 | line: location.line() as c_int, |
| 389 | func: ptr::null_mut(), |
| 390 | hint: ptr::null_mut(), |
| 391 | }; |
| 392 | OwnedPointer::new(err) |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { |
| 397 | unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } |
| 398 | } |
| 399 | |
| 400 | #[test] |
| 401 | fn test_display() { |
| 402 | assert_eq!(&*format!("{}", Error::msg("msg")), "msg"); |
| 403 | assert_eq!(&*format!("{}", Error::msg("msg".to_owned())), "msg"); |
| 404 | assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); |
| 405 | |
| 406 | assert_eq!( |
| 407 | &*format!("{}", Error::with_error("msg", anyhow!("cause"))), |
| 408 | "msg: cause" |
| 409 | ); |
| 410 | } |
| 411 | |
| 412 | #[test] |
| 413 | fn test_bool_or_propagate() { |
| 414 | unsafe { |
| 415 | let mut local_err: *mut bindings::Error = ptr::null_mut(); |
| 416 | |
| 417 | assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); |
| 418 | assert_eq!(local_err, ptr::null_mut()); |
| 419 | |
| 420 | let my_err = Error::msg("msg"); |
| 421 | assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); |
| 422 | assert_ne!(local_err, ptr::null_mut()); |
| 423 | assert_eq!(error_get_pretty(local_err), c"msg"); |
| 424 | bindings::error_free(local_err); |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | #[test] |
| 429 | fn test_ptr_or_propagate() { |
| 430 | unsafe { |
| 431 | let mut local_err: *mut bindings::Error = ptr::null_mut(); |
| 432 | |
| 433 | let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); |
| 434 | assert_eq!(String::from_foreign(ret), "abc"); |
| 435 | assert_eq!(local_err, ptr::null_mut()); |
| 436 | |
| 437 | let my_err = Error::msg("msg"); |
| 438 | assert_eq!( |
| 439 | Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err), |
| 440 | ptr::null_mut() |
| 441 | ); |
| 442 | assert_ne!(local_err, ptr::null_mut()); |
| 443 | assert_eq!(error_get_pretty(local_err), c"msg"); |
| 444 | bindings::error_free(local_err); |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | #[test] |
| 449 | fn test_with_errp() { |
| 450 | unsafe { |
| 451 | let result = Error::with_errp(|_errp| true); |
| 452 | assert_match!(result, Ok(true)); |
| 453 | |
| 454 | let err = Error::with_errp(|errp| { |
| 455 | *errp = error_for_test(c"msg").into_inner(); |
| 456 | false |
| 457 | }) |
| 458 | .unwrap_err(); |
| 459 | assert_eq!(&*format!("{err}"), "msg"); |
| 460 | } |
| 461 | } |
| 462 | } |