xref: /aosp_15_r20/external/avb/rust/src/ops.rs (revision d289c2ba6de359471b23d594623b906876bc48a0)
1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! User callback APIs.
16 //!
17 //! This module is responsible for bridging the user-implemented callbacks so that they can be
18 //! written in safe Rust but libavb can call them from C.
19 
20 extern crate alloc;
21 
22 use crate::{error::result_to_io_enum, CertOps, IoError, IoResult, SHA256_DIGEST_SIZE};
23 use avb_bindgen::{AvbCertOps, AvbCertPermanentAttributes, AvbIOResult, AvbOps};
24 use core::{
25     cmp::min,
26     ffi::{c_char, c_void, CStr},
27     marker::PhantomPinned,
28     pin::Pin,
29     ptr, slice,
30 };
31 #[cfg(feature = "uuid")]
32 use uuid::Uuid;
33 
34 /// Base implementation-provided callbacks for verification.
35 ///
36 /// See libavb `AvbOps` for more complete documentation.
37 ///
38 /// # Lifetimes
39 /// The trait lifetime `'a` indicates the lifetime of any preloaded partition data.
40 ///
41 /// Preloading partitions is an optional feature which allows libavb to use data already loaded to
42 /// RAM rather than allocating memory itself and loading data from disk. Preloading changes the
43 /// data ownership model so that the verification result borrows this existing data rather than
44 /// allocating and owning the data itself. Because of this borrow, we need the lifetime here to
45 /// ensure that the underlying data outlives the verification result object.
46 ///
47 /// If `get_preloaded_partition()` is left unimplemented, all data is loaded and owned by the
48 /// verification result rather than borrowed, and this trait lifetime can be `'static`.
49 pub trait Ops<'a> {
50     /// Reads data from the requested partition on disk.
51     ///
52     /// # Arguments
53     /// * `partition`: partition name to read from.
54     /// * `offset`: offset in bytes within the partition to read from; a positive value indicates an
55     ///             offset from the partition start, a negative value indicates a backwards offset
56     ///             from the partition end.
57     /// * `buffer`: buffer to read data into.
58     ///
59     /// # Returns
60     /// The number of bytes actually read into `buffer` or an `IoError`. Reading less than
61     /// `buffer.len()` bytes is only allowed if the end of the partition was reached.
read_from_partition( &mut self, partition: &CStr, offset: i64, buffer: &mut [u8], ) -> IoResult<usize>62     fn read_from_partition(
63         &mut self,
64         partition: &CStr,
65         offset: i64,
66         buffer: &mut [u8],
67     ) -> IoResult<usize>;
68 
69     /// Returns a reference to preloaded partition contents.
70     ///
71     /// This is an optional optimization if a partition has already been loaded to provide libavb
72     /// with a reference to the data rather than copying it as `read_from_partition()` would.
73     ///
74     /// May be left unimplemented if preloaded partitions are not used.
75     ///
76     /// # Arguments
77     /// * `partition`: partition name to read from.
78     ///
79     /// # Returns
80     /// * A reference to the entire partition contents if the partition has been preloaded.
81     /// * `Err<IoError::NotImplemented>` if the requested partition has not been preloaded;
82     ///   verification will next attempt to load the partition via `read_from_partition()`.
83     /// * Any other `Err<IoError>` if an error occurred; verification will exit immediately.
get_preloaded_partition(&mut self, _partition: &CStr) -> IoResult<&'a [u8]>84     fn get_preloaded_partition(&mut self, _partition: &CStr) -> IoResult<&'a [u8]> {
85         Err(IoError::NotImplemented)
86     }
87 
88     /// Checks if the given public key is valid for vbmeta image signing.
89     ///
90     /// If using libavb_cert, this should forward to `cert_validate_vbmeta_public_key()`.
91     ///
92     /// # Arguments
93     /// * `public_key`: the public key.
94     /// * `public_key_metadata`: public key metadata set by the `--public_key_metadata` arg in
95     ///                          `avbtool`, or None if no metadata was provided.
96     ///
97     /// # Returns
98     /// True if the given key is valid, false if it is not, `IoError` on error.
validate_vbmeta_public_key( &mut self, public_key: &[u8], public_key_metadata: Option<&[u8]>, ) -> IoResult<bool>99     fn validate_vbmeta_public_key(
100         &mut self,
101         public_key: &[u8],
102         public_key_metadata: Option<&[u8]>,
103     ) -> IoResult<bool>;
104 
105     /// Reads the rollback index at the given location.
106     ///
107     /// # Arguments
108     /// * `rollback_index_location`: the rollback location.
109     ///
110     /// # Returns
111     /// The rollback index at this location or `IoError` on error.
read_rollback_index(&mut self, rollback_index_location: usize) -> IoResult<u64>112     fn read_rollback_index(&mut self, rollback_index_location: usize) -> IoResult<u64>;
113 
114     /// Writes the rollback index at the given location.
115     ///
116     /// This API is never actually used by libavb; the purpose of having it here is to group it
117     /// with `read_rollback_index()` and indicate to the implementation that it is responsible
118     /// for providing this functionality. However, it's up to the implementation to call this
119     /// function at the proper time after verification, which is a device-specific decision that
120     /// depends on things like the A/B strategy. See the libavb documentation for more information.
121     ///
122     /// # Arguments
123     /// * `rollback_index_location`: the rollback location.
124     /// * `index`: the rollback index to write.
125     ///
126     /// # Returns
127     /// Unit on success or `IoError` on error.
write_rollback_index(&mut self, rollback_index_location: usize, index: u64) -> IoResult<()>128     fn write_rollback_index(&mut self, rollback_index_location: usize, index: u64) -> IoResult<()>;
129 
130     /// Returns the device unlock state.
131     ///
132     /// # Returns
133     /// True if the device is unlocked, false if locked, `IoError` on error.
read_is_device_unlocked(&mut self) -> IoResult<bool>134     fn read_is_device_unlocked(&mut self) -> IoResult<bool>;
135 
136     /// Returns the GUID of the requested partition.
137     ///
138     /// This is only necessary if the kernel commandline requires GUID substitution, and is omitted
139     /// from the library by default to avoid unnecessary dependencies. To implement:
140     /// 1. Enable the `uuid` feature during compilation
141     /// 2. Provide the [`uuid` crate](https://docs.rs/uuid/latest/uuid/) dependency
142     ///
143     /// # Arguments
144     /// * `partition`: partition name.
145     ///
146     /// # Returns
147     /// The partition GUID or `IoError` on error.
148     #[cfg(feature = "uuid")]
get_unique_guid_for_partition(&mut self, partition: &CStr) -> IoResult<Uuid>149     fn get_unique_guid_for_partition(&mut self, partition: &CStr) -> IoResult<Uuid>;
150 
151     /// Returns the size of the requested partition.
152     ///
153     /// # Arguments
154     /// * `partition`: partition name.
155     ///
156     /// # Returns
157     /// The partition size in bytes or `IoError` on error.
get_size_of_partition(&mut self, partition: &CStr) -> IoResult<u64>158     fn get_size_of_partition(&mut self, partition: &CStr) -> IoResult<u64>;
159 
160     /// Reads the requested persistent value.
161     ///
162     /// This is only necessary if using persistent digests or the "managed restart and EIO"
163     /// hashtree verification mode; if verification is not using these features, this function will
164     /// never be called.
165     ///
166     /// # Arguments
167     /// * `name`: persistent value name.
168     /// * `value`: buffer to read persistent value into; if too small to hold the persistent value,
169     ///            `IoError::InsufficientSpace` should be returned and this function will be called
170     ///            again with an appropriately-sized buffer. This may be an empty slice if the
171     ///            caller only wants to query the persistent value size.
172     ///
173     /// # Returns
174     /// * The number of bytes written into `value` on success.
175     /// * `IoError::NoSuchValue` if `name` is not a known persistent value.
176     /// * `IoError::InsufficientSpace` with the required size if the `value` buffer is too small.
177     /// * Any other `IoError` on failure.
read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> IoResult<usize>178     fn read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> IoResult<usize>;
179 
180     /// Writes the requested persistent value.
181     ///
182     /// This is only necessary if using persistent digests or the "managed restart and EIO"
183     /// hashtree verification mode; if verification is not using these features, this function will
184     /// never be called.
185     ///
186     /// # Arguments
187     /// * `name`: persistent value name.
188     /// * `value`: bytes to write as the new value.
189     ///
190     /// # Returns
191     /// * Unit on success.
192     /// * `IoError::NoSuchValue` if `name` is not a supported persistent value.
193     /// * `IoError::InvalidValueSize` if `value` is too large to save as a persistent value.
194     /// * Any other `IoError` on failure.
write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> IoResult<()>195     fn write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> IoResult<()>;
196 
197     /// Erases the requested persistent value.
198     ///
199     /// This is only necessary if using persistent digests or the "managed restart and EIO"
200     /// hashtree verification mode; if verification is not using these features, this function will
201     /// never be called.
202     ///
203     /// If the requested persistent value is already erased, this function is a no-op and should
204     /// return `Ok(())`.
205     ///
206     /// # Arguments
207     /// * `name`: persistent value name.
208     ///
209     /// # Returns
210     /// * Unit on success.
211     /// * `IoError::NoSuchValue` if `name` is not a supported persistent value.
212     /// * Any other `IoError` on failure.
erase_persistent_value(&mut self, name: &CStr) -> IoResult<()>213     fn erase_persistent_value(&mut self, name: &CStr) -> IoResult<()>;
214 
215     /// Checks if the given public key is valid for the given partition.
216     ///
217     /// This is only used if the "no vbmeta" verification flag is passed, meaning the partitions
218     /// to verify have an embedded vbmeta image rather than locating it in a separate vbmeta
219     /// partition. If this flag is not used, the `validate_vbmeta_public_key()` callback is used
220     /// instead, and this function will never be called.
221     ///
222     /// If using libavb_cert for `partition`, this should forward to
223     /// `cert_validate_vbmeta_public_key()`.
224     ///
225     /// # Arguments
226     /// * `partition`: partition name.
227     /// * `public_key`: the public key.
228     /// * `public_key_metadata`: public key metadata set by the `--public_key_metadata` arg in
229     ///                          `avbtool`, or None if no metadata was provided.
230     ///
231     /// # Returns
232     /// On success, returns a `PublicKeyForPartitionInfo` object indicating whether the given
233     /// key is trusted and its rollback index location.
234     ///
235     /// On failure, returns an error.
validate_public_key_for_partition( &mut self, partition: &CStr, public_key: &[u8], public_key_metadata: Option<&[u8]>, ) -> IoResult<PublicKeyForPartitionInfo>236     fn validate_public_key_for_partition(
237         &mut self,
238         partition: &CStr,
239         public_key: &[u8],
240         public_key_metadata: Option<&[u8]>,
241     ) -> IoResult<PublicKeyForPartitionInfo>;
242 
243     /// Returns the libavb_cert certificate ops if supported.
244     ///
245     /// The libavb_cert extension provides some additional key management and authentication support
246     /// APIs, see the cert module documentation for more info.
247     ///
248     /// The default implementation returns `None` to disable cert APIs.
249     ///
250     /// Commonly when using certs the same struct will implement both `Ops` and `CertOps`, in which
251     /// case this can just return `Some(self)`.
252     ///
253     /// Note: changing this return value in the middle of a libavb operation (e.g. from another
254     /// callback) is not recommended; it may cause runtime errors or a panic later on in the
255     /// operation. It's fine to change this return value outside of libavb operations.
256     ///
257     /// # Returns
258     /// The `CertOps` object, or `None` if not supported.
cert_ops(&mut self) -> Option<&mut dyn CertOps>259     fn cert_ops(&mut self) -> Option<&mut dyn CertOps> {
260         None
261     }
262 }
263 
264 /// Info returned from `validate_public_key_for_partition()`.
265 #[derive(Clone, Copy, Debug)]
266 pub struct PublicKeyForPartitionInfo {
267     /// Whether the key is trusted for the given partition..
268     pub trusted: bool,
269     /// The rollback index to use for the given partition.
270     pub rollback_index_location: u32,
271 }
272 
273 /// Provides the logic to bridge between the libavb C ops and our Rust ops.
274 ///
275 /// This struct internally owns the C ops structs, and borrows ownership of the Rust `Ops`. This
276 /// allows us to translate in both directions, from Rust -> C to call into libavb and then back
277 /// from C -> Rust when servicing the callbacks.
278 ///
279 /// The general control flow look like this:
280 ///
281 /// ```ignore
282 /// user calls a Rust API with their `Ops` {
283 ///     we create `OpsBridge` wrapping `Ops` and the C structs
284 ///     we call into C libavb API {
285 ///         libavb makes C ops callback {
286 ///             we retrieve `OpsBridge` from the callback `user_data` param
287 ///             we make the corresponding Rust `Ops` callback
288 ///         }
289 ///         ... libavb makes more callbacks as needed ...
290 ///     }
291 /// }
292 /// ```
293 ///
294 /// # Lifetimes
295 /// * `'o`: lifetime of the `Ops` object
296 /// * `'p`: lifetime of any preloaded data provided by `Ops`
297 pub(crate) struct OpsBridge<'o, 'p> {
298     /// C `AvbOps` holds a raw pointer to the `OpsBridge` so we can retrieve it during callbacks.
299     avb_ops: AvbOps,
300     /// When using libavb_cert, C `AvbOps`/`AvbCertOps` hold circular pointers to each other.
301     cert_ops: AvbCertOps,
302     /// Rust `Ops` implementation, which may also provide Rust `CertOps`.
303     rust_ops: &'o mut dyn Ops<'p>,
304     /// Remove the `Unpin` trait to indicate this type has address-sensitive state.
305     _pin: PhantomPinned,
306 }
307 
308 impl<'o, 'p> OpsBridge<'o, 'p> {
new(ops: &'o mut dyn Ops<'p>) -> Self309     pub(crate) fn new(ops: &'o mut dyn Ops<'p>) -> Self {
310         Self {
311             avb_ops: AvbOps {
312                 user_data: ptr::null_mut(), // Set at the time of use.
313                 ab_ops: ptr::null_mut(),    // Deprecated, no need to support.
314                 cert_ops: ptr::null_mut(),  // Set at the time of use.
315                 read_from_partition: Some(read_from_partition),
316                 get_preloaded_partition: Some(get_preloaded_partition),
317                 write_to_partition: None, // Not needed, only used for deprecated A/B.
318                 validate_vbmeta_public_key: Some(validate_vbmeta_public_key),
319                 read_rollback_index: Some(read_rollback_index),
320                 write_rollback_index: Some(write_rollback_index),
321                 read_is_device_unlocked: Some(read_is_device_unlocked),
322                 get_unique_guid_for_partition: Some(get_unique_guid_for_partition),
323                 get_size_of_partition: Some(get_size_of_partition),
324                 read_persistent_value: Some(read_persistent_value),
325                 write_persistent_value: Some(write_persistent_value),
326                 validate_public_key_for_partition: Some(validate_public_key_for_partition),
327             },
328             cert_ops: AvbCertOps {
329                 ops: ptr::null_mut(), // Set at the time of use.
330                 read_permanent_attributes: Some(read_permanent_attributes),
331                 read_permanent_attributes_hash: Some(read_permanent_attributes_hash),
332                 set_key_version: Some(set_key_version),
333                 get_random: Some(get_random),
334             },
335             rust_ops: ops,
336             _pin: PhantomPinned,
337         }
338     }
339 
340     /// Initializes and returns the C `AvbOps` structure from an `OpsBridge`.
341     ///
342     /// If the contained `Ops` supports `CertOps`, the returned `AvbOps` will also be configured
343     /// properly for libavb_cert.
344     ///
345     /// Pinning is necessary here because the returned `AvbOps` contains pointers into `self`, so
346     /// we cannot allow `self` to subsequently move or else the pointers would become invalid.
347     ///
348     /// # Returns
349     /// The C `AvbOps` struct to make libavb calls with.
init_and_get_c_ops<'a>(self: Pin<&'a mut Self>) -> &'a mut AvbOps350     pub(crate) fn init_and_get_c_ops<'a>(self: Pin<&'a mut Self>) -> &'a mut AvbOps {
351         // SAFETY: we do not move out of `self_mut`, but only set pointers to pinned addresses.
352         let self_mut = unsafe { self.get_unchecked_mut() };
353 
354         // Set the C `user_data` to point back to us so we can retrieve ourself in callbacks.
355         self_mut.avb_ops.user_data = self_mut as *mut _ as *mut _;
356 
357         // If the `Ops` supports certs, set up the necessary additional pointer tracking.
358         if self_mut.rust_ops.cert_ops().is_some() {
359             self_mut.avb_ops.cert_ops = &mut self_mut.cert_ops;
360             self_mut.cert_ops.ops = &mut self_mut.avb_ops;
361         }
362 
363         &mut self_mut.avb_ops
364     }
365 }
366 
367 /// Extracts the user-provided `Ops` from a raw `AvbOps`.
368 ///
369 /// This function is used in libavb callbacks to bridge libavb's raw C `AvbOps` struct to our Rust
370 /// implementation.
371 ///
372 /// # Arguments
373 /// * `avb_ops`: The raw `AvbOps` pointer used by libavb.
374 ///
375 /// # Returns
376 /// The Rust `Ops` extracted from `avb_ops.user_data`.
377 ///
378 /// # Safety
379 /// * only call this function on an `AvbOps` created via `OpsBridge`
380 /// * drop all references to the returned `Ops` and preloaded data before returning control to
381 ///   libavb or calling this function again
382 ///
383 /// In practice, these conditions are met since we call this at most once in each callback
384 /// to extract the `Ops`, and drop the references at callback completion.
385 ///
386 /// # Lifetimes
387 /// * `'o`: lifetime of the `Ops` object
388 /// * `'p`: lifetime of any preloaded data provided by `Ops`
389 ///
390 /// It's difficult to accurately provide the lifetimes when calling this function, since we are in
391 /// a C callback which provides no lifetime information in the args. We solve this in the safety
392 /// requirements by requiring the caller to drop both references before returning, which is always
393 /// a subset of the actual object lifetimes as the objects must remain valid while libavb is
394 /// actively using them:
395 ///
396 /// ```ignore
397 /// ops/preloaded lifetime {  // Actual 'o/'p start
398 ///   call into libavb {
399 ///     libavb callbacks {
400 ///       as_ops()            // as_ops() 'o/'p start
401 ///     }                     // as_ops() 'o/'p end
402 ///   }
403 /// }                         // Actual 'o/'p end
404 /// ```
as_ops<'o, 'p>(avb_ops: *mut AvbOps) -> IoResult<&'o mut dyn Ops<'p>>405 unsafe fn as_ops<'o, 'p>(avb_ops: *mut AvbOps) -> IoResult<&'o mut dyn Ops<'p>> {
406     // SAFETY: we created this AvbOps object and passed it to libavb so we know it meets all
407     // the criteria for `as_mut()`.
408     let avb_ops = unsafe { avb_ops.as_mut() }.ok_or(IoError::Io)?;
409     // Cast the void* `user_data` back to a OpsBridge*.
410     let bridge = avb_ops.user_data as *mut OpsBridge;
411     // SAFETY: we created this OpsBridge object and passed it to libavb so we know it meets all
412     // the criteria for `as_mut()`.
413     Ok(unsafe { bridge.as_mut() }.ok_or(IoError::Io)?.rust_ops)
414 }
415 
416 /// Similar to `as_ops()`, but for `CertOps`.
417 ///
418 /// # Safety
419 /// Same as `as_ops()`.
as_cert_ops<'o>(cert_ops: *mut AvbCertOps) -> IoResult<&'o mut dyn CertOps>420 unsafe fn as_cert_ops<'o>(cert_ops: *mut AvbCertOps) -> IoResult<&'o mut dyn CertOps> {
421     // SAFETY: we created this `CertOps` object and passed it to libavb so we know it meets all
422     // the criteria for `as_mut()`.
423     let cert_ops = unsafe { cert_ops.as_mut() }.ok_or(IoError::Io)?;
424 
425     // SAFETY: caller must adhere to `as_ops()` safety requirements.
426     let ops = unsafe { as_ops(cert_ops.ops) }?;
427 
428     // Return the `CertOps` implementation. If it doesn't exist here, it indicates an internal error
429     // in this library; somewhere we accepted a non-cert `Ops` into a function that requires cert.
430     ops.cert_ops().ok_or(IoError::NotImplemented)
431 }
432 
433 /// Converts a non-NULL `ptr` to `()`, NULL to `Err(IoError::Io)`.
check_nonnull<T>(ptr: *const T) -> IoResult<()>434 fn check_nonnull<T>(ptr: *const T) -> IoResult<()> {
435     match ptr.is_null() {
436         true => Err(IoError::Io),
437         false => Ok(()),
438     }
439 }
440 
441 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
442 ///
443 /// See corresponding `try_*` function docs.
read_from_partition( ops: *mut AvbOps, partition: *const c_char, offset: i64, num_bytes: usize, buffer: *mut c_void, out_num_read: *mut usize, ) -> AvbIOResult444 unsafe extern "C" fn read_from_partition(
445     ops: *mut AvbOps,
446     partition: *const c_char,
447     offset: i64,
448     num_bytes: usize,
449     buffer: *mut c_void,
450     out_num_read: *mut usize,
451 ) -> AvbIOResult {
452     // SAFETY: see corresponding `try_*` function safety documentation.
453     unsafe {
454         result_to_io_enum(try_read_from_partition(
455             ops,
456             partition,
457             offset,
458             num_bytes,
459             buffer,
460             out_num_read,
461         ))
462     }
463 }
464 
465 /// Bounces the C callback into the user-provided Rust implementation.
466 ///
467 /// # Safety
468 /// * `ops` must have been created via `OpsBridge`.
469 /// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
470 /// * `buffer` must adhere to the requirements of `slice::from_raw_parts_mut()`.
471 /// * `out_num_read` must adhere to the requirements of `ptr::write()`.
try_read_from_partition( ops: *mut AvbOps, partition: *const c_char, offset: i64, num_bytes: usize, buffer: *mut c_void, out_num_read: *mut usize, ) -> IoResult<()>472 unsafe fn try_read_from_partition(
473     ops: *mut AvbOps,
474     partition: *const c_char,
475     offset: i64,
476     num_bytes: usize,
477     buffer: *mut c_void,
478     out_num_read: *mut usize,
479 ) -> IoResult<()> {
480     check_nonnull(partition)?;
481     check_nonnull(buffer)?;
482     check_nonnull(out_num_read)?;
483 
484     // Initialize the output variables first in case something fails.
485     // SAFETY:
486     // * we've checked that the pointer is non-NULL.
487     // * libavb gives us a properly-allocated `out_num_read`.
488     unsafe { ptr::write(out_num_read, 0) };
489 
490     // SAFETY:
491     // * we only use `ops` objects created via `OpsBridge` as required.
492     // * `ops` is only extracted once and is dropped at the end of the callback.
493     let ops = unsafe { as_ops(ops) }?;
494     // SAFETY:
495     // * we've checked that the pointer is non-NULL.
496     // * libavb gives us a properly-allocated and nul-terminated `partition`.
497     // * the string contents are not modified while the returned `&CStr` exists.
498     // * the returned `&CStr` is not held past the scope of this callback.
499     let partition = unsafe { CStr::from_ptr(partition) };
500     // SAFETY:
501     // * we've checked that the pointer is non-NULL.
502     // * libavb gives us a properly-allocated `buffer` with size `num_bytes`.
503     // * we only access the contents via the returned slice.
504     // * the returned slice is not held past the scope of this callback.
505     let buffer = unsafe { slice::from_raw_parts_mut(buffer as *mut u8, num_bytes) };
506 
507     let bytes_read = ops.read_from_partition(partition, offset, buffer)?;
508     // SAFETY:
509     // * we've checked that the pointer is non-NULL.
510     // * libavb gives us a properly-allocated `out_num_read`.
511     unsafe { ptr::write(out_num_read, bytes_read) };
512     Ok(())
513 }
514 
515 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
516 ///
517 /// See corresponding `try_*` function docs.
get_preloaded_partition( ops: *mut AvbOps, partition: *const c_char, num_bytes: usize, out_pointer: *mut *mut u8, out_num_bytes_preloaded: *mut usize, ) -> AvbIOResult518 unsafe extern "C" fn get_preloaded_partition(
519     ops: *mut AvbOps,
520     partition: *const c_char,
521     num_bytes: usize,
522     out_pointer: *mut *mut u8,
523     out_num_bytes_preloaded: *mut usize,
524 ) -> AvbIOResult {
525     // SAFETY: see corresponding `try_*` function safety documentation.
526     unsafe {
527         result_to_io_enum(try_get_preloaded_partition(
528             ops,
529             partition,
530             num_bytes,
531             out_pointer,
532             out_num_bytes_preloaded,
533         ))
534     }
535 }
536 
537 /// Bounces the C callback into the user-provided Rust implementation.
538 ///
539 /// # Safety
540 /// * `ops` must have been created via `OpsBridge`.
541 /// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
542 /// * `out_pointer` and `out_num_bytes_preloaded` must adhere to the requirements of `ptr::write()`.
543 /// * `out_pointer` will become an alias to the `ops` preloaded partition data, so the preloaded
544 ///   data must remain valid and unmodified while `out_pointer` exists.
try_get_preloaded_partition( ops: *mut AvbOps, partition: *const c_char, num_bytes: usize, out_pointer: *mut *mut u8, out_num_bytes_preloaded: *mut usize, ) -> IoResult<()>545 unsafe fn try_get_preloaded_partition(
546     ops: *mut AvbOps,
547     partition: *const c_char,
548     num_bytes: usize,
549     out_pointer: *mut *mut u8,
550     out_num_bytes_preloaded: *mut usize,
551 ) -> IoResult<()> {
552     check_nonnull(partition)?;
553     check_nonnull(out_pointer)?;
554     check_nonnull(out_num_bytes_preloaded)?;
555 
556     // Initialize the output variables first in case something fails.
557     // SAFETY:
558     // * we've checked that the pointers are non-NULL.
559     // * libavb gives us properly-aligned and sized `out` vars.
560     unsafe {
561         ptr::write(out_pointer, ptr::null_mut());
562         ptr::write(out_num_bytes_preloaded, 0);
563     }
564 
565     // SAFETY:
566     // * we only use `ops` objects created via `OpsBridge` as required.
567     // * `ops` is only extracted once and is dropped at the end of the callback.
568     let ops = unsafe { as_ops(ops) }?;
569     // SAFETY:
570     // * we've checked that the pointer is non-NULL.
571     // * libavb gives us a properly-allocated and nul-terminated `partition`.
572     // * the string contents are not modified while the returned `&CStr` exists.
573     // * the returned `&CStr` is not held past the scope of this callback.
574     let partition = unsafe { CStr::from_ptr(partition) };
575 
576     match ops.get_preloaded_partition(partition) {
577         // SAFETY:
578         // * we've checked that the pointers are non-NULL.
579         // * libavb gives us properly-aligned and sized `out` vars.
580         Ok(contents) => unsafe {
581             ptr::write(
582                 out_pointer,
583                 // Warning: we are casting an immutable &[u8] to a mutable *u8. If libavb actually
584                 // modified these contents this could cause undefined behavior, but it just reads.
585                 // TODO: can we change the libavb API to take a const*?
586                 contents.as_ptr() as *mut u8,
587             );
588             ptr::write(
589                 out_num_bytes_preloaded,
590                 // Truncate here if necessary, we may have more preloaded data than libavb needs.
591                 min(contents.len(), num_bytes),
592             );
593         },
594         // No-op if this partition is not preloaded, we've already reset the out variables to
595         // indicate preloaded data is not available.
596         Err(IoError::NotImplemented) => (),
597         Err(e) => return Err(e),
598     };
599     Ok(())
600 }
601 
602 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
603 ///
604 /// See corresponding `try_*` function docs.
validate_vbmeta_public_key( ops: *mut AvbOps, public_key_data: *const u8, public_key_length: usize, public_key_metadata: *const u8, public_key_metadata_length: usize, out_is_trusted: *mut bool, ) -> AvbIOResult605 unsafe extern "C" fn validate_vbmeta_public_key(
606     ops: *mut AvbOps,
607     public_key_data: *const u8,
608     public_key_length: usize,
609     public_key_metadata: *const u8,
610     public_key_metadata_length: usize,
611     out_is_trusted: *mut bool,
612 ) -> AvbIOResult {
613     // SAFETY: see corresponding `try_*` function safety documentation.
614     unsafe {
615         result_to_io_enum(try_validate_vbmeta_public_key(
616             ops,
617             public_key_data,
618             public_key_length,
619             public_key_metadata,
620             public_key_metadata_length,
621             out_is_trusted,
622         ))
623     }
624 }
625 
626 /// Bounces the C callback into the user-provided Rust implementation.
627 ///
628 /// # Safety
629 /// * `ops` must have been created via `OpsBridge`.
630 /// * `public_key_*` args must adhere to the requirements of `slice::from_raw_parts()`.
631 /// * `out_is_trusted` must adhere to the requirements of `ptr::write()`.
try_validate_vbmeta_public_key( ops: *mut AvbOps, public_key_data: *const u8, public_key_length: usize, public_key_metadata: *const u8, public_key_metadata_length: usize, out_is_trusted: *mut bool, ) -> IoResult<()>632 unsafe fn try_validate_vbmeta_public_key(
633     ops: *mut AvbOps,
634     public_key_data: *const u8,
635     public_key_length: usize,
636     public_key_metadata: *const u8,
637     public_key_metadata_length: usize,
638     out_is_trusted: *mut bool,
639 ) -> IoResult<()> {
640     check_nonnull(public_key_data)?;
641     check_nonnull(out_is_trusted)?;
642 
643     // Initialize the output variables first in case something fails.
644     // SAFETY:
645     // * we've checked that the pointer is non-NULL.
646     // * libavb gives us a properly-allocated `out_is_trusted`.
647     unsafe { ptr::write(out_is_trusted, false) };
648 
649     // SAFETY:
650     // * we only use `ops` objects created via `OpsBridge` as required.
651     // * `ops` is only extracted once and is dropped at the end of the callback.
652     let ops = unsafe { as_ops(ops) }?;
653     // SAFETY:
654     // * we've checked that the pointer is non-NULL.
655     // * libavb gives us a properly-allocated `public_key_data` with size `public_key_length`.
656     // * we only access the contents via the returned slice.
657     // * the returned slice is not held past the scope of this callback.
658     let public_key = unsafe { slice::from_raw_parts(public_key_data, public_key_length) };
659     let metadata = check_nonnull(public_key_metadata).ok().map(
660         // SAFETY:
661         // * we've checked that the pointer is non-NULL.
662         // * libavb gives us a properly-allocated `public_key_metadata` with size
663         //   `public_key_metadata_length`.
664         // * we only access the contents via the returned slice.
665         // * the returned slice is not held past the scope of this callback.
666         |_| unsafe { slice::from_raw_parts(public_key_metadata, public_key_metadata_length) },
667     );
668 
669     let trusted = ops.validate_vbmeta_public_key(public_key, metadata)?;
670 
671     // SAFETY:
672     // * we've checked that the pointer is non-NULL.
673     // * libavb gives us a properly-allocated `out_is_trusted`.
674     unsafe { ptr::write(out_is_trusted, trusted) };
675     Ok(())
676 }
677 
678 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
679 ///
680 /// See corresponding `try_*` function docs.
read_rollback_index( ops: *mut AvbOps, rollback_index_location: usize, out_rollback_index: *mut u64, ) -> AvbIOResult681 unsafe extern "C" fn read_rollback_index(
682     ops: *mut AvbOps,
683     rollback_index_location: usize,
684     out_rollback_index: *mut u64,
685 ) -> AvbIOResult {
686     // SAFETY: see corresponding `try_*` function safety documentation.
687     unsafe {
688         result_to_io_enum(try_read_rollback_index(
689             ops,
690             rollback_index_location,
691             out_rollback_index,
692         ))
693     }
694 }
695 
696 /// Bounces the C callback into the user-provided Rust implementation.
697 ///
698 /// # Safety
699 /// * `ops` must have been created via `OpsBridge`.
700 /// * `out_rollback_index` must adhere to the requirements of `ptr::write()`.
try_read_rollback_index( ops: *mut AvbOps, rollback_index_location: usize, out_rollback_index: *mut u64, ) -> IoResult<()>701 unsafe fn try_read_rollback_index(
702     ops: *mut AvbOps,
703     rollback_index_location: usize,
704     out_rollback_index: *mut u64,
705 ) -> IoResult<()> {
706     check_nonnull(out_rollback_index)?;
707 
708     // Initialize the output variables first in case something fails.
709     // SAFETY:
710     // * we've checked that the pointer is non-NULL.
711     // * libavb gives us a properly-allocated `out_rollback_index`.
712     unsafe { ptr::write(out_rollback_index, 0) };
713 
714     // SAFETY:
715     // * we only use `ops` objects created via `OpsBridge` as required.
716     // * `ops` is only extracted once and is dropped at the end of the callback.
717     let ops = unsafe { as_ops(ops) }?;
718     let index = ops.read_rollback_index(rollback_index_location)?;
719 
720     // SAFETY:
721     // * we've checked that the pointer is non-NULL.
722     // * libavb gives us a properly-allocated `out_rollback_index`.
723     unsafe { ptr::write(out_rollback_index, index) };
724     Ok(())
725 }
726 
727 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
728 ///
729 /// See corresponding `try_*` function docs.
write_rollback_index( ops: *mut AvbOps, rollback_index_location: usize, rollback_index: u64, ) -> AvbIOResult730 unsafe extern "C" fn write_rollback_index(
731     ops: *mut AvbOps,
732     rollback_index_location: usize,
733     rollback_index: u64,
734 ) -> AvbIOResult {
735     // SAFETY: see corresponding `try_*` function safety documentation.
736     unsafe {
737         result_to_io_enum(try_write_rollback_index(
738             ops,
739             rollback_index_location,
740             rollback_index,
741         ))
742     }
743 }
744 
745 /// Bounces the C callback into the user-provided Rust implementation.
746 ///
747 /// # Safety
748 /// * `ops` must have been created via `OpsBridge`.
try_write_rollback_index( ops: *mut AvbOps, rollback_index_location: usize, rollback_index: u64, ) -> IoResult<()>749 unsafe fn try_write_rollback_index(
750     ops: *mut AvbOps,
751     rollback_index_location: usize,
752     rollback_index: u64,
753 ) -> IoResult<()> {
754     // SAFETY:
755     // * we only use `ops` objects created via `OpsBridge` as required.
756     // * `ops` is only extracted once and is dropped at the end of the callback.
757     let ops = unsafe { as_ops(ops) }?;
758     ops.write_rollback_index(rollback_index_location, rollback_index)
759 }
760 
761 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
762 ///
763 /// See corresponding `try_*` function docs.
read_is_device_unlocked( ops: *mut AvbOps, out_is_unlocked: *mut bool, ) -> AvbIOResult764 unsafe extern "C" fn read_is_device_unlocked(
765     ops: *mut AvbOps,
766     out_is_unlocked: *mut bool,
767 ) -> AvbIOResult {
768     // SAFETY: see corresponding `try_*` function safety documentation.
769     unsafe { result_to_io_enum(try_read_is_device_unlocked(ops, out_is_unlocked)) }
770 }
771 
772 /// Bounces the C callback into the user-provided Rust implementation.
773 ///
774 /// # Safety
775 /// * `ops` must have been created via `OpsBridge`.
776 /// * `out_is_unlocked` must adhere to the requirements of `ptr::write()`.
try_read_is_device_unlocked( ops: *mut AvbOps, out_is_unlocked: *mut bool, ) -> IoResult<()>777 unsafe fn try_read_is_device_unlocked(
778     ops: *mut AvbOps,
779     out_is_unlocked: *mut bool,
780 ) -> IoResult<()> {
781     check_nonnull(out_is_unlocked)?;
782 
783     // Initialize the output variables first in case something fails.
784     // SAFETY:
785     // * we've checked that the pointer is non-NULL.
786     // * libavb gives us a properly-allocated `out_is_unlocked`.
787     unsafe { ptr::write(out_is_unlocked, false) };
788 
789     // SAFETY:
790     // * we only use `ops` objects created via `OpsBridge` as required.
791     // * `ops` is only extracted once and is dropped at the end of the callback.
792     let ops = unsafe { as_ops(ops) }?;
793     let unlocked = ops.read_is_device_unlocked()?;
794 
795     // SAFETY:
796     // * we've checked that the pointer is non-NULL.
797     // * libavb gives us a properly-allocated `out_is_unlocked`.
798     unsafe { ptr::write(out_is_unlocked, unlocked) };
799     Ok(())
800 }
801 
802 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
803 ///
804 /// See corresponding `try_*` function docs.
get_unique_guid_for_partition( ops: *mut AvbOps, partition: *const c_char, guid_buf: *mut c_char, guid_buf_size: usize, ) -> AvbIOResult805 unsafe extern "C" fn get_unique_guid_for_partition(
806     ops: *mut AvbOps,
807     partition: *const c_char,
808     guid_buf: *mut c_char,
809     guid_buf_size: usize,
810 ) -> AvbIOResult {
811     // SAFETY: see corresponding `try_*` function safety documentation.
812     unsafe {
813         result_to_io_enum(try_get_unique_guid_for_partition(
814             ops,
815             partition,
816             guid_buf,
817             guid_buf_size,
818         ))
819     }
820 }
821 
822 /// Bounces the C callback into the user-provided Rust implementation.
823 ///
824 /// When the `uuid` feature is not enabled, this doesn't call into the user ops at all and instead
825 /// gives the empty string for all partitions.
826 ///
827 /// # Safety
828 /// * `ops` must have been created via `OpsBridge`.
829 /// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
830 /// * `guid_buf` must adhere to the requirements of `slice::from_raw_parts_mut()`.
try_get_unique_guid_for_partition( #[allow(unused_variables)] ops: *mut AvbOps, #[allow(unused_variables)] partition: *const c_char, guid_buf: *mut c_char, guid_buf_size: usize, ) -> IoResult<()>831 unsafe fn try_get_unique_guid_for_partition(
832     #[allow(unused_variables)] ops: *mut AvbOps,
833     #[allow(unused_variables)] partition: *const c_char,
834     guid_buf: *mut c_char,
835     guid_buf_size: usize,
836 ) -> IoResult<()> {
837     check_nonnull(guid_buf)?;
838 
839     // On some architectures `c_char` is `u8`, and on others `i8`. We make sure it's `u8` here
840     // since that's what `CStr::to_bytes_with_nul()` always provides.
841     #[allow(clippy::unnecessary_cast)]
842     let guid_buf = guid_buf as *mut u8;
843 
844     // SAFETY:
845     // * we've checked that the pointer is non-NULL.
846     // * libavb gives us a properly-allocated `guid_buf` with size `guid_buf_size`.
847     // * we only access the contents via the returned slice.
848     // * the returned slice is not held past the scope of this callback.
849     let buffer = unsafe { slice::from_raw_parts_mut(guid_buf, guid_buf_size) };
850 
851     // Initialize the output buffer to the empty string.
852     //
853     // When the `uuid` feature is not selected, the user doesn't need commandline GUIDs but libavb
854     // may still attempt to inject the `vmbeta` or `boot` partition GUIDs into the commandline,
855     // depending on the verification settings. In order to satisfy libavb's requirements we must:
856     // * write a nul-terminated string to avoid undefined behavior (empty string is sufficient)
857     // * return `Ok(())` or verification will fail
858     if buffer.is_empty() {
859         return Err(IoError::Oom);
860     }
861     buffer[0] = b'\0';
862 
863     #[cfg(feature = "uuid")]
864     {
865         check_nonnull(partition)?;
866 
867         // SAFETY:
868         // * we've checked that the pointer is non-NULL.
869         // * libavb gives us a properly-allocated and nul-terminated `partition`.
870         // * the string contents are not modified while the returned `&CStr` exists.
871         // * the returned `&CStr` is not held past the scope of this callback.
872         let partition = unsafe { CStr::from_ptr(partition) };
873 
874         // SAFETY:
875         // * we only use `ops` objects created via `OpsBridge` as required.
876         // * `ops` is only extracted once and is dropped at the end of the callback.
877         let ops = unsafe { as_ops(ops) }?;
878         let guid = ops.get_unique_guid_for_partition(partition)?;
879 
880         // Write the UUID string to a uuid buffer which is guaranteed to be large enough, then use
881         // `CString` to apply nul-termination.
882         // This does allocate memory, but it's short-lived and discarded as soon as we copy the
883         // properly-terminated string back to the buffer.
884         let mut encode_buffer = Uuid::encode_buffer();
885         let guid_str = guid.as_hyphenated().encode_lower(&mut encode_buffer);
886         let guid_cstring = alloc::ffi::CString::new(guid_str.as_bytes()).or(Err(IoError::Io))?;
887         let guid_bytes = guid_cstring.to_bytes_with_nul();
888 
889         if buffer.len() < guid_bytes.len() {
890             // This would indicate some internal error - the uuid library needs more
891             // space to print the UUID string than libavb provided.
892             return Err(IoError::Oom);
893         }
894         buffer[..guid_bytes.len()].copy_from_slice(guid_bytes);
895     }
896 
897     Ok(())
898 }
899 
900 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
901 ///
902 /// See corresponding `try_*` function docs.
get_size_of_partition( ops: *mut AvbOps, partition: *const c_char, out_size_num_bytes: *mut u64, ) -> AvbIOResult903 unsafe extern "C" fn get_size_of_partition(
904     ops: *mut AvbOps,
905     partition: *const c_char,
906     out_size_num_bytes: *mut u64,
907 ) -> AvbIOResult {
908     // SAFETY: see corresponding `try_*` function safety documentation.
909     unsafe {
910         result_to_io_enum(try_get_size_of_partition(
911             ops,
912             partition,
913             out_size_num_bytes,
914         ))
915     }
916 }
917 
918 /// Bounces the C callback into the user-provided Rust implementation.
919 ///
920 /// # Safety
921 /// * `ops` must have been created via `OpsBridge`.
922 /// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
923 /// * `out_size_num_bytes` must adhere to the requirements of `ptr::write()`.
try_get_size_of_partition( ops: *mut AvbOps, partition: *const c_char, out_size_num_bytes: *mut u64, ) -> IoResult<()>924 unsafe fn try_get_size_of_partition(
925     ops: *mut AvbOps,
926     partition: *const c_char,
927     out_size_num_bytes: *mut u64,
928 ) -> IoResult<()> {
929     check_nonnull(partition)?;
930     check_nonnull(out_size_num_bytes)?;
931 
932     // Initialize the output variables first in case something fails.
933     // SAFETY:
934     // * we've checked that the pointer is non-NULL.
935     // * libavb gives us a properly-allocated `out_size_num_bytes`.
936     unsafe { ptr::write(out_size_num_bytes, 0) };
937 
938     // SAFETY:
939     // * we only use `ops` objects created via `OpsBridge` as required.
940     // * `ops` is only extracted once and is dropped at the end of the callback.
941     let ops = unsafe { as_ops(ops) }?;
942     // SAFETY:
943     // * we've checked that the pointer is non-NULL.
944     // * libavb gives us a properly-allocated and nul-terminated `partition`.
945     // * the string contents are not modified while the returned `&CStr` exists.
946     // * the returned `&CStr` is not held past the scope of this callback.
947     let partition = unsafe { CStr::from_ptr(partition) };
948     let size = ops.get_size_of_partition(partition)?;
949 
950     // SAFETY:
951     // * we've checked that the pointer is non-NULL.
952     // * libavb gives us a properly-allocated `out_size_num_bytes`.
953     unsafe { ptr::write(out_size_num_bytes, size) };
954     Ok(())
955 }
956 
957 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
958 ///
959 /// See corresponding `try_*` function docs.
read_persistent_value( ops: *mut AvbOps, name: *const c_char, buffer_size: usize, out_buffer: *mut u8, out_num_bytes_read: *mut usize, ) -> AvbIOResult960 unsafe extern "C" fn read_persistent_value(
961     ops: *mut AvbOps,
962     name: *const c_char,
963     buffer_size: usize,
964     out_buffer: *mut u8,
965     out_num_bytes_read: *mut usize,
966 ) -> AvbIOResult {
967     // SAFETY: see corresponding `try_*` function safety documentation.
968     unsafe {
969         result_to_io_enum(try_read_persistent_value(
970             ops,
971             name,
972             buffer_size,
973             out_buffer,
974             out_num_bytes_read,
975         ))
976     }
977 }
978 
979 /// Bounces the C callback into the user-provided Rust implementation.
980 ///
981 /// # Safety
982 /// * `ops` must have been created via `OpsBridge`.
983 /// * `name` must adhere to the requirements of `CStr::from_ptr()`.
984 /// * `out_buffer` must adhere to the requirements of `slice::from_raw_parts_mut()`.
985 /// * `out_num_bytes_read` must adhere to the requirements of `ptr::write()`.
try_read_persistent_value( ops: *mut AvbOps, name: *const c_char, buffer_size: usize, out_buffer: *mut u8, out_num_bytes_read: *mut usize, ) -> IoResult<()>986 unsafe fn try_read_persistent_value(
987     ops: *mut AvbOps,
988     name: *const c_char,
989     buffer_size: usize,
990     out_buffer: *mut u8,
991     out_num_bytes_read: *mut usize,
992 ) -> IoResult<()> {
993     check_nonnull(name)?;
994     check_nonnull(out_num_bytes_read)?;
995 
996     // Initialize the output variables first in case something fails.
997     // SAFETY:
998     // * we've checked that the pointer is non-NULL.
999     // * libavb gives us a properly-allocated `out_num_bytes_read`.
1000     unsafe { ptr::write(out_num_bytes_read, 0) };
1001 
1002     // SAFETY:
1003     // * we only use `ops` objects created via `OpsBridge` as required.
1004     // * `ops` is only extracted once and is dropped at the end of the callback.
1005     let ops = unsafe { as_ops(ops) }?;
1006     // SAFETY:
1007     // * we've checked that the pointer is non-NULL.
1008     // * libavb gives us a properly-allocated and nul-terminated `name`.
1009     // * the string contents are not modified while the returned `&CStr` exists.
1010     // * the returned `&CStr` is not held past the scope of this callback.
1011     let name = unsafe { CStr::from_ptr(name) };
1012     let mut empty: [u8; 0] = [];
1013     let value = match out_buffer.is_null() {
1014         // NULL buffer => empty slice, used to just query the value size.
1015         true => &mut empty,
1016         false => {
1017             // SAFETY:
1018             // * we've checked that the pointer is non-NULL.
1019             // * libavb gives us a properly-allocated `out_buffer` with size `buffer_size`.
1020             // * we only access the contents via the returned slice.
1021             // * the returned slice is not held past the scope of this callback.
1022             unsafe { slice::from_raw_parts_mut(out_buffer, buffer_size) }
1023         }
1024     };
1025 
1026     let result = ops.read_persistent_value(name, value);
1027     // On success or insufficient space we need to write the property size back.
1028     if let Ok(size) | Err(IoError::InsufficientSpace(size)) = result {
1029         // SAFETY:
1030         // * we've checked that the pointer is non-NULL.
1031         // * libavb gives us a properly-allocated `out_num_bytes_read`.
1032         unsafe { ptr::write(out_num_bytes_read, size) };
1033     };
1034     // We've written the size back and can drop it now.
1035     result.map(|_| ())
1036 }
1037 
1038 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
1039 ///
1040 /// See corresponding `try_*` function docs.
write_persistent_value( ops: *mut AvbOps, name: *const c_char, value_size: usize, value: *const u8, ) -> AvbIOResult1041 unsafe extern "C" fn write_persistent_value(
1042     ops: *mut AvbOps,
1043     name: *const c_char,
1044     value_size: usize,
1045     value: *const u8,
1046 ) -> AvbIOResult {
1047     // SAFETY: see corresponding `try_*` function safety documentation.
1048     unsafe { result_to_io_enum(try_write_persistent_value(ops, name, value_size, value)) }
1049 }
1050 
1051 /// Bounces the C callback into the user-provided Rust implementation.
1052 ///
1053 /// # Safety
1054 /// * `ops` must have been created via `OpsBridge`.
1055 /// * `name` must adhere to the requirements of `CStr::from_ptr()`.
1056 /// * `out_buffer` must adhere to the requirements of `slice::from_raw_parts()`.
try_write_persistent_value( ops: *mut AvbOps, name: *const c_char, value_size: usize, value: *const u8, ) -> IoResult<()>1057 unsafe fn try_write_persistent_value(
1058     ops: *mut AvbOps,
1059     name: *const c_char,
1060     value_size: usize,
1061     value: *const u8,
1062 ) -> IoResult<()> {
1063     check_nonnull(name)?;
1064 
1065     // SAFETY:
1066     // * we only use `ops` objects created via `OpsBridge` as required.
1067     // * `ops` is only extracted once and is dropped at the end of the callback.
1068     let ops = unsafe { as_ops(ops) }?;
1069     // SAFETY:
1070     // * we've checked that the pointer is non-NULL.
1071     // * libavb gives us a properly-allocated and nul-terminated `name`.
1072     // * the string contents are not modified while the returned `&CStr` exists.
1073     // * the returned `&CStr` is not held past the scope of this callback.
1074     let name = unsafe { CStr::from_ptr(name) };
1075 
1076     if value_size == 0 {
1077         ops.erase_persistent_value(name)
1078     } else {
1079         check_nonnull(value)?;
1080         // SAFETY:
1081         // * we've checked that the pointer is non-NULL.
1082         // * libavb gives us a properly-allocated `value` with size `value_size`.
1083         // * we only access the contents via the returned slice.
1084         // * the returned slice is not held past the scope of this callback.
1085         let value = unsafe { slice::from_raw_parts(value, value_size) };
1086         ops.write_persistent_value(name, value)
1087     }
1088 }
1089 
1090 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
1091 ///
1092 /// See corresponding `try_*` function docs.
validate_public_key_for_partition( ops: *mut AvbOps, partition: *const c_char, public_key_data: *const u8, public_key_length: usize, public_key_metadata: *const u8, public_key_metadata_length: usize, out_is_trusted: *mut bool, out_rollback_index_location: *mut u32, ) -> AvbIOResult1093 unsafe extern "C" fn validate_public_key_for_partition(
1094     ops: *mut AvbOps,
1095     partition: *const c_char,
1096     public_key_data: *const u8,
1097     public_key_length: usize,
1098     public_key_metadata: *const u8,
1099     public_key_metadata_length: usize,
1100     out_is_trusted: *mut bool,
1101     out_rollback_index_location: *mut u32,
1102 ) -> AvbIOResult {
1103     // SAFETY: see corresponding `try_*` function safety documentation.
1104     unsafe {
1105         result_to_io_enum(try_validate_public_key_for_partition(
1106             ops,
1107             partition,
1108             public_key_data,
1109             public_key_length,
1110             public_key_metadata,
1111             public_key_metadata_length,
1112             out_is_trusted,
1113             out_rollback_index_location,
1114         ))
1115     }
1116 }
1117 
1118 /// Bounces the C callback into the user-provided Rust implementation.
1119 ///
1120 /// # Safety
1121 /// * `ops` must have been created via `OpsBridge`.
1122 /// * `partition` must adhere to the requirements of `CStr::from_ptr()`.
1123 /// * `public_key_*` args must adhere to the requirements of `slice::from_raw_parts()`.
1124 /// * `out_*` must adhere to the requirements of `ptr::write()`.
1125 #[allow(clippy::too_many_arguments)] // Mirroring libavb C API.
try_validate_public_key_for_partition( ops: *mut AvbOps, partition: *const c_char, public_key_data: *const u8, public_key_length: usize, public_key_metadata: *const u8, public_key_metadata_length: usize, out_is_trusted: *mut bool, out_rollback_index_location: *mut u32, ) -> IoResult<()>1126 unsafe fn try_validate_public_key_for_partition(
1127     ops: *mut AvbOps,
1128     partition: *const c_char,
1129     public_key_data: *const u8,
1130     public_key_length: usize,
1131     public_key_metadata: *const u8,
1132     public_key_metadata_length: usize,
1133     out_is_trusted: *mut bool,
1134     out_rollback_index_location: *mut u32,
1135 ) -> IoResult<()> {
1136     check_nonnull(partition)?;
1137     check_nonnull(public_key_data)?;
1138     check_nonnull(out_is_trusted)?;
1139     check_nonnull(out_rollback_index_location)?;
1140 
1141     // Initialize the output variables first in case something fails.
1142     // SAFETY:
1143     // * we've checked that the pointers are non-NULL.
1144     // * libavb gives us a properly-allocated `out_*`.
1145     unsafe {
1146         ptr::write(out_is_trusted, false);
1147         ptr::write(out_rollback_index_location, 0);
1148     }
1149 
1150     // SAFETY:
1151     // * we only use `ops` objects created via `OpsBridge` as required.
1152     // * `ops` is only extracted once and is dropped at the end of the callback.
1153     let ops = unsafe { as_ops(ops) }?;
1154     // SAFETY:
1155     // * we've checked that the pointer is non-NULL.
1156     // * libavb gives us a properly-allocated and nul-terminated `partition`.
1157     // * the string contents are not modified while the returned `&CStr` exists.
1158     // * the returned `&CStr` is not held past the scope of this callback.
1159     let partition = unsafe { CStr::from_ptr(partition) };
1160     // SAFETY:
1161     // * we've checked that the pointer is non-NULL.
1162     // * libavb gives us a properly-allocated `public_key_data` with size `public_key_length`.
1163     // * we only access the contents via the returned slice.
1164     // * the returned slice is not held past the scope of this callback.
1165     let public_key = unsafe { slice::from_raw_parts(public_key_data, public_key_length) };
1166     let metadata = check_nonnull(public_key_metadata).ok().map(
1167         // SAFETY:
1168         // * we've checked that the pointer is non-NULL.
1169         // * libavb gives us a properly-allocated `public_key_metadata` with size
1170         //   `public_key_metadata_length`.
1171         // * we only access the contents via the returned slice.
1172         // * the returned slice is not held past the scope of this callback.
1173         |_| unsafe { slice::from_raw_parts(public_key_metadata, public_key_metadata_length) },
1174     );
1175 
1176     let key_info = ops.validate_public_key_for_partition(partition, public_key, metadata)?;
1177 
1178     // SAFETY:
1179     // * we've checked that the pointers are non-NULL.
1180     // * libavb gives us a properly-allocated `out_*`.
1181     unsafe {
1182         ptr::write(out_is_trusted, key_info.trusted);
1183         ptr::write(
1184             out_rollback_index_location,
1185             key_info.rollback_index_location,
1186         );
1187     }
1188     Ok(())
1189 }
1190 
1191 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
1192 ///
1193 /// See corresponding `try_*` function docs.
read_permanent_attributes( cert_ops: *mut AvbCertOps, attributes: *mut AvbCertPermanentAttributes, ) -> AvbIOResult1194 unsafe extern "C" fn read_permanent_attributes(
1195     cert_ops: *mut AvbCertOps,
1196     attributes: *mut AvbCertPermanentAttributes,
1197 ) -> AvbIOResult {
1198     result_to_io_enum(
1199         // SAFETY: see corresponding `try_*` function safety documentation.
1200         unsafe { try_read_permanent_attributes(cert_ops, attributes) },
1201     )
1202 }
1203 
1204 /// Bounces the C callback into the user-provided Rust implementation.
1205 ///
1206 /// # Safety
1207 /// * `cert_ops` must have been created via `ScopedAvbOps`.
1208 /// * `attributes` must be a valid `AvbCertPermanentAttributes` that we have exclusive access to.
try_read_permanent_attributes( cert_ops: *mut AvbCertOps, attributes: *mut AvbCertPermanentAttributes, ) -> IoResult<()>1209 unsafe fn try_read_permanent_attributes(
1210     cert_ops: *mut AvbCertOps,
1211     attributes: *mut AvbCertPermanentAttributes,
1212 ) -> IoResult<()> {
1213     // SAFETY: `attributes` is a valid object provided by libavb that we have exclusive access to.
1214     let attributes = unsafe { attributes.as_mut() }.ok_or(IoError::Io)?;
1215 
1216     // SAFETY:
1217     // * we only use `cert_ops` objects created via `ScopedAvbOps` as required.
1218     // * `cert_ops` is only extracted once and is dropped at the end of the callback.
1219     let cert_ops = unsafe { as_cert_ops(cert_ops) }?;
1220     cert_ops.read_permanent_attributes(attributes)
1221 }
1222 
1223 /// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
1224 ///
1225 /// See corresponding `try_*` function docs.
read_permanent_attributes_hash( cert_ops: *mut AvbCertOps, hash: *mut u8, ) -> AvbIOResult1226 unsafe extern "C" fn read_permanent_attributes_hash(
1227     cert_ops: *mut AvbCertOps,
1228     hash: *mut u8,
1229 ) -> AvbIOResult {
1230     result_to_io_enum(
1231         // SAFETY: see corresponding `try_*` function safety documentation.
1232         unsafe { try_read_permanent_attributes_hash(cert_ops, hash) },
1233     )
1234 }
1235 
1236 /// Bounces the C callback into the user-provided Rust implementation.
1237 ///
1238 /// # Safety
1239 /// * `cert_ops` must have been created via `ScopedAvbOps`.
1240 /// * `hash` must point to a valid buffer of size `SHA256_DIGEST_SIZE` that we have exclusive
1241 ///   access to.
try_read_permanent_attributes_hash( cert_ops: *mut AvbCertOps, hash: *mut u8, ) -> IoResult<()>1242 unsafe fn try_read_permanent_attributes_hash(
1243     cert_ops: *mut AvbCertOps,
1244     hash: *mut u8,
1245 ) -> IoResult<()> {
1246     check_nonnull(hash)?;
1247 
1248     // SAFETY:
1249     // * we only use `cert_ops` objects created via `ScopedAvbOps` as required.
1250     // * `cert_ops` is only extracted once and is dropped at the end of the callback.
1251     let cert_ops = unsafe { as_cert_ops(cert_ops) }?;
1252     let provided_hash = cert_ops.read_permanent_attributes_hash()?;
1253 
1254     // SAFETY:
1255     // * `provided_hash` is a valid `[u8]` with size `SHA256_DIGEST_SIZE`.
1256     // * libavb gives us a properly-allocated `hash` with size `SHA256_DIGEST_SIZE`.
1257     // * the arrays are independent objects and cannot overlap.
1258     unsafe { ptr::copy_nonoverlapping(provided_hash.as_ptr(), hash, SHA256_DIGEST_SIZE) };
1259 
1260     Ok(())
1261 }
1262 
1263 /// Wraps a callback to convert the given `IoResult<>` to `None` for libavb.
1264 ///
1265 /// See corresponding `try_*` function docs.
set_key_version( cert_ops: *mut AvbCertOps, rollback_index_location: usize, key_version: u64, )1266 unsafe extern "C" fn set_key_version(
1267     cert_ops: *mut AvbCertOps,
1268     rollback_index_location: usize,
1269     key_version: u64,
1270 ) {
1271     // SAFETY: see corresponding `try_*` function safety documentation.
1272     let result = unsafe { try_set_key_version(cert_ops, rollback_index_location, key_version) };
1273 
1274     // `set_key_version()` is unique in that it has no return value, and therefore cannot fail.
1275     // However, our internal C -> Rust logic does have some potential failure points when we unwrap
1276     // the C pointers to extract our Rust objects.
1277     //
1278     // Ignoring the error could be a security risk, as it would silently prevent the device from
1279     // updating key rollback versions, so instead we panic here.
1280     if let Err(e) = result {
1281         panic!("Fatal error in set_key_version(): {:?}", e);
1282     }
1283 }
1284 
1285 /// Bounces the C callback into the user-provided Rust implementation.
1286 ///
1287 /// # Safety
1288 /// `cert_ops` must have been created via `ScopedAvbOps`.
try_set_key_version( cert_ops: *mut AvbCertOps, rollback_index_location: usize, key_version: u64, ) -> IoResult<()>1289 unsafe fn try_set_key_version(
1290     cert_ops: *mut AvbCertOps,
1291     rollback_index_location: usize,
1292     key_version: u64,
1293 ) -> IoResult<()> {
1294     // SAFETY:
1295     // * we only use `cert_ops` objects created via `ScopedAvbOps` as required.
1296     // * `cert_ops` is only extracted once and is dropped at the end of the callback.
1297     let cert_ops = unsafe { as_cert_ops(cert_ops) }?;
1298     cert_ops.set_key_version(rollback_index_location, key_version);
1299     Ok(())
1300 }
1301 
1302 /// Wraps a callback to convert the given `IoResult<>` to `None` for libavb.
1303 ///
1304 /// See corresponding `try_*` function docs.
get_random( cert_ops: *mut AvbCertOps, num_bytes: usize, output: *mut u8, ) -> AvbIOResult1305 unsafe extern "C" fn get_random(
1306     cert_ops: *mut AvbCertOps,
1307     num_bytes: usize,
1308     output: *mut u8,
1309 ) -> AvbIOResult {
1310     result_to_io_enum(
1311         // SAFETY: see corresponding `try_*` function safety documentation.
1312         unsafe { try_get_random(cert_ops, num_bytes, output) },
1313     )
1314 }
1315 
1316 /// Bounces the C callback into the user-provided Rust implementation.
1317 ///
1318 /// # Safety
1319 /// * `cert_ops` must have been created via `ScopedAvbOps`.
1320 /// * `output` must point to a valid buffer of size `num_bytes` that we have exclusive access to.
try_get_random( cert_ops: *mut AvbCertOps, num_bytes: usize, output: *mut u8, ) -> IoResult<()>1321 unsafe fn try_get_random(
1322     cert_ops: *mut AvbCertOps,
1323     num_bytes: usize,
1324     output: *mut u8,
1325 ) -> IoResult<()> {
1326     check_nonnull(output)?;
1327     if num_bytes == 0 {
1328         return Ok(());
1329     }
1330 
1331     // SAFETY:
1332     // * we've checked that the pointer is non-NULL.
1333     // * libavb gives us a properly-allocated `output` with size `num_bytes`.
1334     // * we only access the contents via the returned slice.
1335     // * the returned slice is not held past the scope of this callback.
1336     let output = unsafe { slice::from_raw_parts_mut(output, num_bytes) };
1337 
1338     // SAFETY:
1339     // * we only use `cert_ops` objects created via `ScopedAvbOps` as required.
1340     // * `cert_ops` is only extracted once and is dropped at the end of the callback.
1341     let cert_ops = unsafe { as_cert_ops(cert_ops) }?;
1342     cert_ops.get_random(output)
1343 }
1344