aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Pursell <dpursell@google.com>2024-04-03 10:09:03 -0700
committerDavid Pursell <dpursell@google.com>2024-04-23 10:03:07 -0700
commitea6cfa7402d5786e65c1a89c0dcff69181baebf6 (patch)
treedf2640c80e39b19b0302788c3436d064bd2621cc
parent1f25d8a42a4e7120d63b2b10b2bf4b3722cc5efd (diff)
downloadavb-ea6cfa7402d5786e65c1a89c0dcff69181baebf6.tar.gz
libavb_rs: split Ops/CertOps into separate traits
CertOps is now a separate trait rather than a supertrait of Ops, which is a little simpler and allows more usage flexibility at the cost of runtime checking for cert support rather than compile-time. Additionally, cleans up the internal bookkeeping mechanisms to simplify `ScopedAvbOps` and `UserData` into a single `OpsBridge` struct. Bug: b/320543206 Test: atest libavb_rs_test libavb_rs_uuid_test libavb_rs_unittest libavb_rs_uuid_unittest Change-Id: I2f4ba35f55a8610929dcb57d96dcd133b06a7aea
-rw-r--r--rust/src/cert.rs17
-rw-r--r--rust/src/ops.rs206
-rw-r--r--rust/src/verify.rs9
3 files changed, 124 insertions, 108 deletions
diff --git a/rust/src/cert.rs b/rust/src/cert.rs
index adf682e..52be5bf 100644
--- a/rust/src/cert.rs
+++ b/rust/src/cert.rs
@@ -107,8 +107,8 @@ pub const CERT_PIK_VERSION_LOCATION: usize = avb_bindgen::AVB_CERT_PIK_VERSION_L
/// If using libavb_cert, make sure no vbmetas use this location, it must be reserved for the PSK.
pub const CERT_PSK_VERSION_LOCATION: usize = avb_bindgen::AVB_CERT_PSK_VERSION_LOCATION as usize;
-/// libavb_cert additional callbacks.
-pub trait CertOps<'a>: Ops<'a> {
+/// libavb_cert extension callbacks.
+pub trait CertOps {
/// Reads the device's permanent attributes.
///
/// The full permanent attributes are not required to be securely stored; corruption of this
@@ -198,14 +198,14 @@ pub trait CertOps<'a>: Ops<'a> {
/// specific partitions.
///
/// # Arguments
-/// * `ops`: the `CertOps` callback implementations.
+/// * `ops`: the `Ops` callback implementations, which must provide a `cert_ops()` implementation.
/// * `public_key`: the public key.
/// * `public_key_metadata`: public key metadata.
///
/// # Returns
/// True if the given key is valid, false if it is not, `IoError` on error.
pub fn cert_validate_vbmeta_public_key(
- _ops: &mut dyn CertOps,
+ _ops: &mut dyn Ops,
_public_key: &[u8],
_public_key_metadata: Option<&[u8]>,
) -> IoResult<bool> {
@@ -237,15 +237,18 @@ pub fn cert_generate_unlock_challenge(
/// device's permanent attributes.
///
/// # Arguments
-/// * `cert_ops`: the `CertOps` callback implementations.
+/// * `ops`: the `Ops` callback implementations, which must provide a `cert_ops()` implementation.
/// * `credential`: the signed unlock credential to verify.
///
/// # Returns
/// * `Ok(true)` if the credential validated
/// * `Ok(false)` if it failed validation
-/// * `Err(IoError)` on `cert_ops` failure
+/// * `Err(IoError)` on `ops` failure
pub fn cert_validate_unlock_credential(
- _cert_ops: &mut dyn CertOps,
+ // Note: in the libavb C API this function takes an `AvbCertOps` rather than `AvbOps`, but
+ // the implementation requires both, so we need an `Ops` here. This is also more consistent
+ // with `validate_vbmeta_public_key()` which similarly requires both but takes `AvbOps`.
+ _ops: &mut dyn Ops,
_credential: &CertUnlockCredential,
) -> IoResult<bool> {
// TODO(b/320543206): implement
diff --git a/rust/src/ops.rs b/rust/src/ops.rs
index 10f2b1a..88e9d95 100644
--- a/rust/src/ops.rs
+++ b/rust/src/ops.rs
@@ -19,12 +19,13 @@
extern crate alloc;
-use crate::{error::result_to_io_enum, IoError, IoResult};
-use avb_bindgen::{AvbIOResult, AvbOps};
+use crate::{error::result_to_io_enum, CertOps, IoError, IoResult};
+use avb_bindgen::{AvbCertOps, AvbIOResult, AvbOps};
use core::{
cmp::min,
ffi::{c_char, c_void, CStr},
- marker::PhantomData,
+ marker::PhantomPinned,
+ pin::Pin,
ptr, slice,
};
#[cfg(feature = "uuid")]
@@ -238,6 +239,22 @@ pub trait Ops<'a> {
public_key: &[u8],
public_key_metadata: Option<&[u8]>,
) -> IoResult<PublicKeyForPartitionInfo>;
+
+ /// Returns the libavb_cert certificate ops if supported.
+ ///
+ /// The libavb_cert extension provides some additional key management and authentication support
+ /// APIs, see the cert module documentation for more info.
+ ///
+ /// The default implementation returns `None` to disable cert APIs.
+ ///
+ /// Commonly when using certs the same struct will implement both `Ops` and `CertOps`, in which
+ /// case this can just return `Some(self)`.
+ ///
+ /// # Returns
+ /// The `CertOps` object, or `None` if not supported.
+ fn cert_ops(&mut self) -> Option<&mut dyn CertOps> {
+ None
+ }
}
/// Info returned from `validate_public_key_for_partition()`.
@@ -249,80 +266,48 @@ pub struct PublicKeyForPartitionInfo {
pub rollback_index_location: u32,
}
-/// Helper to pass user-provided `Ops` through libavb via the `user_data` pointer.
+/// Provides the logic to bridge between the libavb C ops and our Rust ops.
///
-/// This is a bit tricky in Rust, we can't just cast `Ops` to `void*` and back directly because
-/// `Ops` is a trait to be implemented by the user, which means we don't know the concrete type
-/// at this point and must use dynamic dispatch.
+/// This struct internally owns the C ops structs, and borrows ownership of the Rust `Ops`. This
+/// allows us to translate in both directions, from Rust -> C to call into libavb and then back
+/// from C -> Rust when servicing the callbacks.
///
-/// However, dynamic dispatch in Rust requires a "fat pointer" (2 pointers) which cannot be cast to
-/// a single `void*`. So instead, we wrap the dynamic dispatch inside this struct which _can_ be
-/// represented as a single `void*`, and then we can unwrap it again to fetch the original
-/// `&dyn Ops`.
+/// The general control flow look like this:
///
-/// A more typical approach is to use `Box` to heap-allocate the `&dyn` and then pass the `Box`
-/// around, but we want to avoid allocation as much as possible.
-///
-/// Control flow:
-/// ```
-/// user libavb_rs libavb
-/// -----------------------------------------------------------------------------------------------
-/// create `Ops` (Rust) with
-/// callback implementations
-/// ---->
-/// `UserData::new()` wraps:
-/// `Ops` (Rust/fat) ->
-/// `UserData` (Rust/thin)
-///
-/// `ScopedAvbOps` makes
-/// `AvbOps` (C) containing:
-/// 1. `UserData*` (C)
-/// 2. our callbacks (C)
-/// ---->
-/// execute `AvbOps` (C)
-/// callbacks as needed
-/// <----
-/// `as_ops()` unwraps:
-/// `AvbOps` (C) ->
-/// `UserData` (Rust/thin) ->
-/// `Ops` (Rust/fat)
-///
-/// Convert callback data to
-/// safe Rust
-/// <----
-/// perform `Ops` (Rust)
-/// callback
+/// ```ignore
+/// user calls a Rust API with their `Ops` {
+/// we create `OpsBridge` wrapping `Ops` and the C structs
+/// we call into C libavb API {
+/// libavb makes C ops callback {
+/// we retrieve `OpsBridge` from the callback `user_data` param
+/// we make the corresponding Rust `Ops` callback
+/// }
+/// ... libavb makes more callbacks as needed ...
+/// }
+/// }
/// ```
///
/// # Lifetimes
/// * `'o`: lifetime of the `Ops` object
/// * `'p`: lifetime of any preloaded data provided by `Ops`
-pub(crate) struct UserData<'o, 'p>(&'o mut dyn Ops<'p>);
-
-impl<'o, 'p> UserData<'o, 'p> {
- pub(crate) fn new(ops: &'o mut dyn Ops<'p>) -> Self {
- Self(ops)
- }
-}
-
-/// Wraps the C `AvbOps` struct with lifetime information for the compiler.
-pub(crate) struct ScopedAvbOps<'o, 'p> {
- /// `AvbOps` holds a raw pointer to `UserData` with no lifetime information.
+pub(crate) struct OpsBridge<'o, 'p> {
+ /// C `AvbOps` holds a raw pointer to the `OpsBridge` so we can retrieve it during callbacks.
avb_ops: AvbOps,
- /// This provides the necessary lifetime information so the compiler can make sure that
- /// the `UserData` stays alive at least as long as we do.
- _user_data: PhantomData<UserData<'o, 'p>>,
+ /// When using libavb_cert, C `AvbOps`/`AvbCertOps` hold circular pointers to each other.
+ cert_ops: AvbCertOps,
+ /// Rust `Ops` implementation, which may also provide Rust `CertOps`.
+ rust_ops: &'o mut dyn Ops<'p>,
+ /// Remove the `Unpin` trait to indicate this type has address-sensitive state.
+ _pin: PhantomPinned,
}
-impl<'o, 'p> ScopedAvbOps<'o, 'p> {
- pub(crate) fn new(user_data: &mut UserData<'o, 'p>) -> Self {
+impl<'o, 'p> OpsBridge<'o, 'p> {
+ pub(crate) fn new(ops: &'o mut dyn Ops<'p>) -> Self {
Self {
avb_ops: AvbOps {
- // Rust won't transitively cast so we need to cast twice manually, but the compiler
- // is smart enough to deduce the types we need.
- user_data: user_data as *mut _ as *mut _,
- ab_ops: ptr::null_mut(), // Deprecated, no need to support.
- cert_ops: ptr::null_mut(), // TODO: support optional libavb_cert.
+ user_data: ptr::null_mut(), // Set at the time of use.
+ ab_ops: ptr::null_mut(), // Deprecated, no need to support.
+ cert_ops: ptr::null_mut(), // Set at the time of use.
read_from_partition: Some(read_from_partition),
get_preloaded_partition: Some(get_preloaded_partition),
write_to_partition: None, // Not needed, only used for deprecated A/B.
@@ -336,14 +321,43 @@ impl<'o, 'p> ScopedAvbOps<'o, 'p> {
write_persistent_value: Some(write_persistent_value),
validate_public_key_for_partition: Some(validate_public_key_for_partition),
},
- _user_data: PhantomData,
+ cert_ops: AvbCertOps {
+ ops: ptr::null_mut(), // Set at the time of use.
+ // TODO(b/320543206): implement the callbacks.
+ read_permanent_attributes: None,
+ read_permanent_attributes_hash: None,
+ set_key_version: None,
+ get_random: None,
+ },
+ rust_ops: ops,
+ _pin: PhantomPinned,
}
}
-}
-impl<'o, 'p> AsMut<AvbOps> for ScopedAvbOps<'o, 'p> {
- fn as_mut(&mut self) -> &mut AvbOps {
- &mut self.avb_ops
+ /// Initializes and returns the C `AvbOps` structure from an `OpsBridge`.
+ ///
+ /// If the contained `Ops` supports `CertOps`, the returned `AvbOps` will also be configured
+ /// properly for libavb_cert.
+ ///
+ /// Pinning is necessary here because the returned `AvbOps` contains pointers into `self`, so
+ /// we cannot allow `self` to subsequently move or else the pointers would become invalid.
+ ///
+ /// # Returns
+ /// The C `AvbOps` struct to make libavb calls with.
+ pub(crate) fn init_and_get_c_ops<'a>(self: Pin<&'a mut Self>) -> &'a mut AvbOps {
+ // SAFETY: we do not move out of `self_mut`, but only set pointers to pinned addresses.
+ let self_mut = unsafe { self.get_unchecked_mut() };
+
+ // Set the C `user_data` to point back to us so we can retrieve ourself in callbacks.
+ self_mut.avb_ops.user_data = self_mut as *mut _ as *mut _;
+
+ // If the `Ops` supports certs, set up the necessary additional pointer tracking.
+ if self_mut.rust_ops.cert_ops().is_some() {
+ self_mut.avb_ops.cert_ops = &mut self_mut.cert_ops;
+ self_mut.cert_ops.ops = &mut self_mut.avb_ops;
+ }
+
+ &mut self_mut.avb_ops
}
}
@@ -359,7 +373,7 @@ impl<'o, 'p> AsMut<AvbOps> for ScopedAvbOps<'o, 'p> {
/// The Rust `Ops` extracted from `avb_ops.user_data`.
///
/// # Safety
-/// * only call this function on an `AvbOps` created via `ScopedAvbOps`
+/// * only call this function on an `AvbOps` created via `OpsBridge`
/// * drop all references to the returned `Ops` and preloaded data before returning control to
/// libavb or calling this function again
///
@@ -389,11 +403,11 @@ unsafe fn as_ops<'o, 'p>(avb_ops: *mut AvbOps) -> IoResult<&'o mut dyn Ops<'p>>
// SAFETY: we created this AvbOps object and passed it to libavb so we know it meets all
// the criteria for `as_mut()`.
let avb_ops = unsafe { avb_ops.as_mut() }.ok_or(IoError::Io)?;
- // Cast the void* `user_data` back to a UserData*.
- let user_data = avb_ops.user_data as *mut UserData;
- // SAFETY: we created this UserData object and passed it to libavb so we know it meets all
+ // Cast the void* `user_data` back to a OpsBridge*.
+ let bridge = avb_ops.user_data as *mut OpsBridge;
+ // SAFETY: we created this OpsBridge object and passed it to libavb so we know it meets all
// the criteria for `as_mut()`.
- Ok(unsafe { user_data.as_mut() }.ok_or(IoError::Io)?.0)
+ Ok(unsafe { bridge.as_mut() }.ok_or(IoError::Io)?.rust_ops)
}
/// Converts a non-NULL `ptr` to `()`, NULL to `Err(IoError::Io)`.
@@ -431,7 +445,7 @@ unsafe extern "C" fn read_from_partition(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
/// * `buffer` must adhere to the requirements of `slice::from_raw_parts_mut()`.
/// * `out_num_read` must adhere to the requirements of `ptr::write()`.
@@ -454,7 +468,7 @@ unsafe fn try_read_from_partition(
unsafe { ptr::write(out_num_read, 0) };
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
// SAFETY:
@@ -503,7 +517,7 @@ unsafe extern "C" fn get_preloaded_partition(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
/// * `out_pointer` and `out_num_bytes_preloaded` must adhere to the requirements of `ptr::write()`.
/// * `out_pointer` will become an alias to the `ops` preloaded partition data, so the preloaded
@@ -529,7 +543,7 @@ unsafe fn try_get_preloaded_partition(
}
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
// SAFETY:
@@ -592,7 +606,7 @@ unsafe extern "C" fn validate_vbmeta_public_key(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `public_key_*` args must adhere to the requirements of `slice::from_raw_parts()`.
/// * `out_is_trusted` must adhere to the requirements of `ptr::write()`.
unsafe fn try_validate_vbmeta_public_key(
@@ -613,7 +627,7 @@ unsafe fn try_validate_vbmeta_public_key(
unsafe { ptr::write(out_is_trusted, false) };
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
// SAFETY:
@@ -662,7 +676,7 @@ unsafe extern "C" fn read_rollback_index(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `out_rollback_index` must adhere to the requirements of `ptr::write()`.
unsafe fn try_read_rollback_index(
ops: *mut AvbOps,
@@ -678,7 +692,7 @@ unsafe fn try_read_rollback_index(
unsafe { ptr::write(out_rollback_index, 0) };
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
let index = ops.read_rollback_index(rollback_index_location)?;
@@ -711,14 +725,14 @@ unsafe extern "C" fn write_rollback_index(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
unsafe fn try_write_rollback_index(
ops: *mut AvbOps,
rollback_index_location: usize,
rollback_index: u64,
) -> IoResult<()> {
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
ops.write_rollback_index(rollback_index_location, rollback_index)
@@ -738,7 +752,7 @@ unsafe extern "C" fn read_is_device_unlocked(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `out_is_unlocked` must adhere to the requirements of `ptr::write()`.
unsafe fn try_read_is_device_unlocked(
ops: *mut AvbOps,
@@ -753,7 +767,7 @@ unsafe fn try_read_is_device_unlocked(
unsafe { ptr::write(out_is_unlocked, false) };
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
let unlocked = ops.read_is_device_unlocked()?;
@@ -791,7 +805,7 @@ unsafe extern "C" fn get_unique_guid_for_partition(
/// gives the empty string for all partitions.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
/// * `guid_buf` must adhere to the requirements of `slice::from_raw_parts_mut()`.
unsafe fn try_get_unique_guid_for_partition(
@@ -838,7 +852,7 @@ unsafe fn try_get_unique_guid_for_partition(
let partition = unsafe { CStr::from_ptr(partition) };
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
let guid = ops.get_unique_guid_for_partition(partition)?;
@@ -884,7 +898,7 @@ unsafe extern "C" fn get_size_of_partition(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
/// * `out_size_num_bytes` must adhere to the requirements of `ptr::write()`.
unsafe fn try_get_size_of_partition(
@@ -902,7 +916,7 @@ unsafe fn try_get_size_of_partition(
unsafe { ptr::write(out_size_num_bytes, 0) };
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
// SAFETY:
@@ -945,7 +959,7 @@ unsafe extern "C" fn read_persistent_value(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `name` must adhere to the requirements of `CStr::from_ptr()`.
/// * `out_buffer` must adhere to the requirements of `slice::from_raw_parts_mut()`.
/// * `out_num_bytes_read` must adhere to the requirements of `ptr::write()`.
@@ -966,7 +980,7 @@ unsafe fn try_read_persistent_value(
unsafe { ptr::write(out_num_bytes_read, 0) };
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
// SAFETY:
@@ -1017,7 +1031,7 @@ unsafe extern "C" fn write_persistent_value(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `name` must adhere to the requirements of `CStr::from_ptr()`.
/// * `out_buffer` must adhere to the requirements of `slice::from_raw_parts()`.
unsafe fn try_write_persistent_value(
@@ -1029,7 +1043,7 @@ unsafe fn try_write_persistent_value(
check_nonnull(name)?;
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
// SAFETY:
@@ -1084,7 +1098,7 @@ unsafe extern "C" fn validate_public_key_for_partition(
/// Bounces the C callback into the user-provided Rust implementation.
///
/// # Safety
-/// * `ops` must have been created via `ScopedAvbOps`.
+/// * `ops` must have been created via `OpsBridge`.
/// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
/// * `public_key_*` args must adhere to the requirements of `slice::from_raw_parts()`.
/// * `out_*` must adhere to the requirements of `ptr::write()`.
@@ -1114,7 +1128,7 @@ unsafe fn try_validate_public_key_for_partition(
}
// SAFETY:
- // * we only use `ops` objects created via `ScopedAvbOps` as required.
+ // * we only use `ops` objects created via `OpsBridge` as required.
// * `ops` is only extracted once and is dropped at the end of the callback.
let ops = unsafe { as_ops(ops) }?;
// SAFETY:
diff --git a/rust/src/verify.rs b/rust/src/verify.rs
index 0098644..b718d63 100644
--- a/rust/src/verify.rs
+++ b/rust/src/verify.rs
@@ -35,6 +35,7 @@ use core::{
ffi::{c_char, CStr},
fmt,
marker::PhantomData,
+ pin::pin,
ptr::{self, null, null_mut, NonNull},
slice,
};
@@ -399,10 +400,6 @@ pub fn slot_verify<'a>(
flags: SlotVerifyFlags,
hashtree_error_mode: HashtreeErrorMode,
) -> SlotVerifyResult<'a, SlotVerifyData<'a>> {
- let mut user_data = ops::UserData::new(ops);
- let mut scoped_ops = ops::ScopedAvbOps::new(&mut user_data);
- let avb_ops = scoped_ops.as_mut();
-
// libavb detects the size of the `requested_partitions` array by NULL termination. Expecting
// the Rust caller to do this would make the API much more awkward, so we populate a
// NULL-terminated array of c-string pointers ourselves. For now we use a fixed-sized array
@@ -420,6 +417,7 @@ pub fn slot_verify<'a>(
// pointer to an empty string in this case, not NULL.
let ab_suffix = ab_suffix.unwrap_or(CStr::from_bytes_with_nul(b"\0").unwrap());
+ let ops_bridge = pin!(ops::OpsBridge::new(ops));
let mut out_data: *mut AvbSlotVerifyData = null_mut();
// Call the libavb verification function.
@@ -429,11 +427,12 @@ pub fn slot_verify<'a>(
// or else the memory will leak.
//
// SAFETY:
+ // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps`.
// * we've properly initialized all objects passed into libavb.
// * if `out_data` is non-null on return, we take ownership via `SlotVerifyData`.
let result = slot_verify_enum_to_result(unsafe {
avb_slot_verify(
- avb_ops,
+ ops_bridge.init_and_get_c_ops(),
partitions_array.as_ptr(),
ab_suffix.as_ptr(),
flags,