libavb: Allow AvbOps functions to fail with OOM. am: 507752b2b5
am: 29e4244edf

Change-Id: Icbac4c62e89792c85975ef581267813d5edc9fbe
diff --git a/boot_control/boot_control_avb.c b/boot_control/boot_control_avb.c
index 403476c..96f445f 100644
--- a/boot_control/boot_control_avb.c
+++ b/boot_control/boot_control_avb.c
@@ -62,7 +62,8 @@
 }
 
 static int module_markBootSuccessful(boot_control_module_t* module) {
-  if (avb_ab_mark_slot_successful(ops, module_getCurrentSlot(module))) {
+  if (avb_ab_mark_slot_successful(ops, module_getCurrentSlot(module)) ==
+      AVB_IO_RESULT_OK) {
     return 0;
   } else {
     return -EIO;
@@ -71,7 +72,7 @@
 
 static int module_setActiveBootSlot(boot_control_module_t* module,
                                     unsigned int slot) {
-  if (avb_ab_mark_slot_active(ops, slot)) {
+  if (avb_ab_mark_slot_active(ops, slot) == AVB_IO_RESULT_OK) {
     return 0;
   } else {
     return -EIO;
@@ -80,7 +81,7 @@
 
 static int module_setSlotAsUnbootable(struct boot_control_module* module,
                                       unsigned int slot) {
-  if (avb_ab_mark_slot_unbootable(ops, slot)) {
+  if (avb_ab_mark_slot_unbootable(ops, slot) == AVB_IO_RESULT_OK) {
     return 0;
   } else {
     return -EIO;
@@ -94,7 +95,7 @@
 
   avb_assert(slot < 2);
 
-  if (!avb_ab_data_read(ops, &ab_data)) {
+  if (avb_ab_data_read(ops, &ab_data) != AVB_IO_RESULT_OK) {
     return -EIO;
   }
 
diff --git a/libavb/avb_ab_flow.c b/libavb/avb_ab_flow.c
index de90a81..7e11e27 100644
--- a/libavb/avb_ab_flow.c
+++ b/libavb/avb_ab_flow.c
@@ -81,17 +81,20 @@
  */
 #define AB_METADATA_MISC_PARTITION_OFFSET 2048
 
-bool avb_ab_data_read(AvbOps* ops, AvbABData* data) {
+AvbIOResult avb_ab_data_read(AvbOps* ops, AvbABData* data) {
   AvbABData serialized;
-  AvbIOResult io_result;
+  AvbIOResult io_ret;
   size_t num_bytes_read;
 
-  io_result =
+  io_ret =
       ops->read_from_partition(ops, "misc", AB_METADATA_MISC_PARTITION_OFFSET,
                                sizeof(AvbABData), &serialized, &num_bytes_read);
-  if (io_result != AVB_IO_RESULT_OK || num_bytes_read != sizeof(AvbABData)) {
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    return AVB_IO_RESULT_ERROR_OOM;
+  } else if (io_ret != AVB_IO_RESULT_OK ||
+             num_bytes_read != sizeof(AvbABData)) {
     avb_error("Error reading A/B metadata.\n");
-    return false;
+    return AVB_IO_RESULT_ERROR_IO;
   }
 
   if (!avb_ab_data_verify_and_byteswap(&serialized, data)) {
@@ -102,22 +105,24 @@
     return avb_ab_data_write(ops, data);
   }
 
-  return true;
+  return AVB_IO_RESULT_OK;
 }
 
-bool avb_ab_data_write(AvbOps* ops, const AvbABData* data) {
+AvbIOResult avb_ab_data_write(AvbOps* ops, const AvbABData* data) {
   AvbABData serialized;
-  AvbIOResult io_result;
+  AvbIOResult io_ret;
 
   avb_ab_data_update_crc_and_byteswap(data, &serialized);
-  io_result =
+  io_ret =
       ops->write_to_partition(ops, "misc", AB_METADATA_MISC_PARTITION_OFFSET,
                               sizeof(AvbABData), &serialized);
-  if (io_result != AVB_IO_RESULT_OK) {
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    return AVB_IO_RESULT_ERROR_OOM;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
     avb_error("Error writing A/B metadata.\n");
-    return false;
+    return AVB_IO_RESULT_ERROR_IO;
   }
-  return true;
+  return AVB_IO_RESULT_OK;
 }
 
 static bool slot_is_bootable(AvbABSlotData* slot) {
@@ -154,12 +159,17 @@
 
 static const char* slot_suffixes[2] = {"_a", "_b"};
 
-/* Helper function to metadata - returns false if an I/O error occurs. */
-static bool load_metadata(AvbOps* ops, AvbABData* ab_data,
-                          AvbABData* ab_data_orig) {
-  if (!avb_ab_data_read(ops, ab_data)) {
+/* Helper function to load metadata - returns AVB_IO_RESULT_OK on
+ * success, error code otherwise.
+ */
+static AvbIOResult load_metadata(AvbOps* ops, AvbABData* ab_data,
+                                 AvbABData* ab_data_orig) {
+  AvbIOResult io_ret;
+
+  io_ret = avb_ab_data_read(ops, ab_data);
+  if (io_ret != AVB_IO_RESULT_OK) {
     avb_error("I/O error while loading A/B metadata.\n");
-    return false;
+    return io_ret;
   }
   *ab_data_orig = *ab_data;
 
@@ -169,22 +179,19 @@
    */
   slot_normalize(&ab_data->slots[0]);
   slot_normalize(&ab_data->slots[1]);
-  return true;
+  return AVB_IO_RESULT_OK;
 }
 
-/* Writes A/B metadata to disk only if it has changed - returns false
- * if an I/O error occurs.
+/* Writes A/B metadata to disk only if it has changed - returns
+ * AVB_IO_RESULT_OK on success, error code otherwise.
  */
-static bool save_metadata_if_changed(AvbOps* ops, AvbABData* ab_data,
-                                     AvbABData* ab_data_orig) {
+static AvbIOResult save_metadata_if_changed(AvbOps* ops, AvbABData* ab_data,
+                                            AvbABData* ab_data_orig) {
   if (avb_safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)) != 0) {
     avb_debug("Writing A/B metadata to disk.\n");
-    if (!avb_ab_data_write(ops, ab_data)) {
-      avb_error("Error writing A/B metadata to disk.\n");
-      return false;
-    }
+    return avb_ab_data_write(ops, ab_data);
   }
-  return true;
+  return AVB_IO_RESULT_OK;
 }
 
 AvbABFlowResult avb_ab_flow(AvbOps* ops, AvbSlotVerifyData** out_data) {
@@ -193,8 +200,13 @@
   AvbABFlowResult ret;
   AvbABData ab_data, ab_data_orig;
   size_t slot_index_to_boot, n;
+  AvbIOResult io_ret;
 
-  if (!load_metadata(ops, &ab_data, &ab_data_orig)) {
+  io_ret = load_metadata(ops, &ab_data, &ab_data_orig);
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+    goto out;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
     ret = AVB_AB_FLOW_RESULT_ERROR_IO;
     goto out;
   }
@@ -205,11 +217,11 @@
       AvbSlotVerifyResult verify_result;
       verify_result = avb_slot_verify(ops, slot_suffixes[n], &slot_data[n]);
       if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
-        if (verify_result == AVB_AB_FLOW_RESULT_ERROR_OOM) {
+        if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_OOM) {
           ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
           goto out;
         }
-        if (verify_result == AVB_AB_FLOW_RESULT_ERROR_IO) {
+        if (verify_result == AVB_SLOT_VERIFY_RESULT_ERROR_IO) {
           ret = AVB_AB_FLOW_RESULT_ERROR_IO;
           goto out;
         }
@@ -260,13 +272,21 @@
 
     if (rollback_index_value != 0) {
       uint64_t current_rollback_index_value;
-      if (!ops->read_rollback_index(ops, n, &current_rollback_index_value)) {
+      io_ret = ops->read_rollback_index(ops, n, &current_rollback_index_value);
+      if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+        ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+        goto out;
+      } else if (io_ret != AVB_IO_RESULT_OK) {
         avb_error("Error getting rollback index for slot.\n");
         ret = AVB_AB_FLOW_RESULT_ERROR_IO;
         goto out;
       }
       if (current_rollback_index_value != rollback_index_value) {
-        if (!ops->write_rollback_index(ops, n, rollback_index_value)) {
+        io_ret = ops->write_rollback_index(ops, n, rollback_index_value);
+        if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+          ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+          goto out;
+        } else if (io_ret != AVB_IO_RESULT_OK) {
           avb_error("Error setting stored rollback index.\n");
           ret = AVB_AB_FLOW_RESULT_ERROR_IO;
           goto out;
@@ -288,8 +308,13 @@
   }
 
 out:
-  if (!save_metadata_if_changed(ops, &ab_data, &ab_data_orig)) {
-    ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+  io_ret = save_metadata_if_changed(ops, &ab_data, &ab_data_orig);
+  if (io_ret != AVB_IO_RESULT_OK) {
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      ret = AVB_AB_FLOW_RESULT_ERROR_OOM;
+    } else {
+      ret = AVB_AB_FLOW_RESULT_ERROR_IO;
+    }
     if (data != NULL) {
       avb_slot_verify_data_free(data);
       data = NULL;
@@ -313,14 +338,15 @@
   return ret;
 }
 
-bool avb_ab_mark_slot_active(AvbOps* ops, unsigned int slot_number) {
+AvbIOResult avb_ab_mark_slot_active(AvbOps* ops, unsigned int slot_number) {
   AvbABData ab_data, ab_data_orig;
-  bool ret = false;
   unsigned int other_slot_number;
+  AvbIOResult ret;
 
   avb_assert(slot_number < 2);
 
-  if (!load_metadata(ops, &ab_data, &ab_data_orig)) {
+  ret = load_metadata(ops, &ab_data, &ab_data_orig);
+  if (ret != AVB_IO_RESULT_OK) {
     goto out;
   }
 
@@ -335,58 +361,62 @@
     ab_data.slots[other_slot_number].priority = AVB_AB_MAX_PRIORITY - 1;
   }
 
-  ret = true;
+  ret = AVB_IO_RESULT_OK;
 
 out:
-  if (!save_metadata_if_changed(ops, &ab_data, &ab_data_orig)) {
-    ret = false;
+  if (ret == AVB_IO_RESULT_OK) {
+    ret = save_metadata_if_changed(ops, &ab_data, &ab_data_orig);
   }
   return ret;
 }
 
-bool avb_ab_mark_slot_unbootable(AvbOps* ops, unsigned int slot_number) {
+AvbIOResult avb_ab_mark_slot_unbootable(AvbOps* ops, unsigned int slot_number) {
   AvbABData ab_data, ab_data_orig;
-  bool ret = false;
+  AvbIOResult ret;
 
   avb_assert(slot_number < 2);
 
-  if (!load_metadata(ops, &ab_data, &ab_data_orig)) {
+  ret = load_metadata(ops, &ab_data, &ab_data_orig);
+  if (ret != AVB_IO_RESULT_OK) {
     goto out;
   }
 
   slot_set_unbootable(&ab_data.slots[slot_number]);
-  ret = true;
+
+  ret = AVB_IO_RESULT_OK;
 
 out:
-  if (!save_metadata_if_changed(ops, &ab_data, &ab_data_orig)) {
-    ret = false;
+  if (ret == AVB_IO_RESULT_OK) {
+    ret = save_metadata_if_changed(ops, &ab_data, &ab_data_orig);
   }
   return ret;
 }
 
-bool avb_ab_mark_slot_successful(AvbOps* ops, unsigned int slot_number) {
+AvbIOResult avb_ab_mark_slot_successful(AvbOps* ops, unsigned int slot_number) {
   AvbABData ab_data, ab_data_orig;
-  bool ret = false;
+  AvbIOResult ret;
 
   avb_assert(slot_number < 2);
 
-  if (!load_metadata(ops, &ab_data, &ab_data_orig)) {
+  ret = load_metadata(ops, &ab_data, &ab_data_orig);
+  if (ret != AVB_IO_RESULT_OK) {
     goto out;
   }
 
   if (!slot_is_bootable(&ab_data.slots[slot_number])) {
     avb_error("Cannot mark unbootable slot as successful.\n");
+    ret = AVB_IO_RESULT_OK;
     goto out;
   }
 
   ab_data.slots[slot_number].tries_remaining = 0;
   ab_data.slots[slot_number].successful_boot = 1;
 
-  ret = true;
+  ret = AVB_IO_RESULT_OK;
 
 out:
-  if (!save_metadata_if_changed(ops, &ab_data, &ab_data_orig)) {
-    ret = false;
+  if (ret == AVB_IO_RESULT_OK) {
+    ret = save_metadata_if_changed(ops, &ab_data, &ab_data_orig);
   }
   return ret;
 }
diff --git a/libavb/avb_ab_flow.h b/libavb/avb_ab_flow.h
index 2ea3380..14600a5 100644
--- a/libavb/avb_ab_flow.h
+++ b/libavb/avb_ab_flow.h
@@ -118,20 +118,20 @@
 void avb_ab_data_init(AvbABData* data);
 
 /* Reads A/B metadata from the 'misc' partition using |ops|. Returned
- * data is properly byteswapped. Returns false if an I/O operation
- * failed.
+ * data is properly byteswapped. Returns AVB_IO_RESULT_OK on
+ * success, error code otherwise.
  *
  * If the data read from disk is invalid (e.g. wrong magic or CRC
  * checksum failure), the metadata will be reset using
  * avb_ab_data_init() and then written to disk.
  */
-bool avb_ab_data_read(AvbOps* ops, AvbABData* data);
+AvbIOResult avb_ab_data_read(AvbOps* ops, AvbABData* data);
 
 /* Writes A/B metadata to the 'misc' partition using |ops|. This will
- * byteswap and update the CRC as needed. Returns false if an I/O
- * error occurs, true otherwise.
+ * byteswap and update the CRC as needed. Returns AVB_IO_RESULT_OK on
+ * success, error code otherwise.
  */
-bool avb_ab_data_write(AvbOps* ops, const AvbABData* data);
+AvbIOResult avb_ab_data_write(AvbOps* ops, const AvbABData* data);
 
 /* Return codes used in avb_ab_flow(), see that function for
  * documentation of each value.
@@ -182,31 +182,34 @@
  */
 AvbABFlowResult avb_ab_flow(AvbOps* ops, AvbSlotVerifyData** out_data);
 
-/* Marks the slot with the given slot number as active. Returns false
- * if the operation fails.
+/* Marks the slot with the given slot number as active. Returns
+ * AVB_IO_RESULT_OK on success, error code otherwise.
  *
  * This function is typically used by the OS updater when completing
  * an update. It can also used by the firmware for implementing the
  * "set_active" command.
  */
-bool avb_ab_mark_slot_active(AvbOps* ops, unsigned int slot_number);
+AvbIOResult avb_ab_mark_slot_active(AvbOps* ops, unsigned int slot_number);
 
 /* Marks the slot with the given slot number as unbootable. Returns
- * false if the operation fails.
+ * AVB_IO_RESULT_OK on success, error code otherwise.
  *
  * This function is typically used by the OS updater before writing to
  * a slot.
  */
-bool avb_ab_mark_slot_unbootable(AvbOps* ops, unsigned int slot_number);
+AvbIOResult avb_ab_mark_slot_unbootable(AvbOps* ops, unsigned int slot_number);
 
 /* Marks the slot with the given slot number as having booted
- * successfully. This has no effect is the slot is not
- * bootable. Returns false if the operation fails.
+ * successfully. Returns AVB_IO_RESULT_OK on success, error code
+ * otherwise.
+ *
+ * Calling this on an unbootable slot is an error - AVB_IO_RESULT_OK
+ * will be returned yet the function will have no side-effects.
  *
  * This function is typically used by the OS updater after having
  * confirmed that the slot works as intended.
  */
-bool avb_ab_mark_slot_successful(AvbOps* ops, unsigned int slot_number);
+AvbIOResult avb_ab_mark_slot_successful(AvbOps* ops, unsigned int slot_number);
 
 #ifdef __cplusplus
 }
diff --git a/libavb/avb_ops.h b/libavb/avb_ops.h
index 5ef658b..f929766 100644
--- a/libavb/avb_ops.h
+++ b/libavb/avb_ops.h
@@ -40,21 +40,24 @@
  * AVB_IO_RESULT_OK is returned if the requested operation was
  * successful.
  *
+ * AVB_IO_RESULT_ERROR_IO is returned if the underlying hardware (disk
+ * or other subsystem) encountered an I/O error.
+ *
+ * AVB_IO_RESULT_ERROR_OOM is returned if unable to allocate memory.
+ *
  * AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION is returned if the requested
  * partition does not exist.
  *
  * AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION is returned if the
  * range of bytes requested to be read or written is outside the range
  * of the partition.
- *
- * AVB_IO_RESULT_ERROR_IO is returned if the underlying disk
- * encountered an I/O error.
  */
 typedef enum {
   AVB_IO_RESULT_OK,
-  AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
-  AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION,
+  AVB_IO_RESULT_ERROR_OOM,
   AVB_IO_RESULT_ERROR_IO,
+  AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION,
+  AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION
 } AvbIOResult;
 
 struct AvbOps;
@@ -110,40 +113,44 @@
    * embedded key material generated with 'avbtool
    * extract_public_key'.
    *
-   * Return true if trusted or false if untrusted.
+   * If AVB_IO_RESULT_OK is returned then |out_is_trusted| is set -
+   * true if trusted or false if untrusted.
    */
-  bool (*validate_vbmeta_public_key)(AvbOps* ops,
-                                     const uint8_t* public_key_data,
-                                     size_t public_key_length);
+  AvbIOResult (*validate_vbmeta_public_key)(AvbOps* ops,
+                                            const uint8_t* public_key_data,
+                                            size_t public_key_length,
+                                            bool* out_is_trusted);
 
   /* Gets the rollback index corresponding to the slot given by
    * |rollback_index_slot|. The value is returned in
-   * |out_rollback_index|. Returns true if the rollback index was
-   * retrieved, false on error.
+   * |out_rollback_index|. Returns AVB_IO_RESULT_OK if the rollback
+   * index was retrieved, otherwise an error code.
    *
    * A device may have a limited amount of rollback index slots (say,
    * one or four) so may error out if |rollback_index_slot| exceeds
    * this number.
    */
-  bool (*read_rollback_index)(AvbOps* ops, size_t rollback_index_slot,
-                              uint64_t* out_rollback_index);
+  AvbIOResult (*read_rollback_index)(AvbOps* ops, size_t rollback_index_slot,
+                                     uint64_t* out_rollback_index);
 
   /* Sets the rollback index corresponding to the slot given by
-   * |rollback_index_slot| to |rollback_index|. Returns true if
-   * the rollback index was set, false on error.
+   * |rollback_index_slot| to |rollback_index|. Returns
+   * AVB_IO_RESULT_OK if the rollback index was set, otherwise an
+   * error code.
    *
    * A device may have a limited amount of rollback index slots (say,
    * one or four) so may error out if |rollback_index_slot| exceeds
    * this number.
    */
-  bool (*write_rollback_index)(AvbOps* ops, size_t rollback_index_slot,
-                               uint64_t rollback_index);
+  AvbIOResult (*write_rollback_index)(AvbOps* ops, size_t rollback_index_slot,
+                                      uint64_t rollback_index);
 
   /* Gets whether the device is unlocked. The value is returned in
    * |out_is_unlocked| (true if unlocked, false otherwise). Returns
-   * true if the state was retrieved, false on error.
+   * AVB_IO_RESULT_OK if the state was retrieved, otherwise an error
+   * code.
    */
-  bool (*read_is_device_unlocked)(AvbOps* ops, bool* out_is_unlocked);
+  AvbIOResult (*read_is_device_unlocked)(AvbOps* ops, bool* out_is_unlocked);
 
   /* Gets the unique partition GUID for a partition with name in
    * |partition| (NUL-terminated UTF-8 string). The GUID is copied as
@@ -153,11 +160,12 @@
    *
    *  527c1c6d-6361-4593-8842-3c78fcd39219
    *
-   * Returns false if the operation fails (no such partition or
-   * |buf_size| is too small), true if it succeeds.
+   * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
    */
-  bool (*get_unique_guid_for_partition)(AvbOps* ops, const char* partition,
-                                        char* guid_buf, size_t guid_buf_size);
+  AvbIOResult (*get_unique_guid_for_partition)(AvbOps* ops,
+                                               const char* partition,
+                                               char* guid_buf,
+                                               size_t guid_buf_size);
 };
 
 #ifdef __cplusplus
diff --git a/libavb/avb_slot_verify.c b/libavb/avb_slot_verify.c
index 12f139e..dec65a4 100644
--- a/libavb/avb_slot_verify.c
+++ b/libavb/avb_slot_verify.c
@@ -88,7 +88,10 @@
   io_ret =
       ops->read_from_partition(ops, part_name, 0 /* offset */,
                                hash_desc.image_size, image_buf, &part_num_read);
-  if (io_ret != AVB_IO_RESULT_OK) {
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
     avb_errorv(part_name, ": Error loading data from partition.\n", NULL);
     ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
     goto out;
@@ -212,7 +215,10 @@
     io_ret =
         ops->read_from_partition(ops, full_partition_name, -AVB_FOOTER_SIZE,
                                  AVB_FOOTER_SIZE, footer_buf, &footer_num_read);
-    if (io_ret != AVB_IO_RESULT_OK) {
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto out;
+    } else if (io_ret != AVB_IO_RESULT_OK) {
       avb_errorv(full_partition_name, ": Error loading footer.\n", NULL);
       ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
       goto out;
@@ -246,7 +252,10 @@
 
   io_ret = ops->read_from_partition(ops, full_partition_name, vbmeta_offset,
                                     vbmeta_size, vbmeta_buf, &vbmeta_num_read);
-  if (io_ret != AVB_IO_RESULT_OK) {
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
     avb_errorv(full_partition_name, ": Error loading vbmeta data.\n", NULL);
     ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
     goto out;
@@ -277,8 +286,22 @@
       goto out;
     }
   } else {
+    bool key_is_trusted = false;
+
     avb_assert(is_main_vbmeta);
-    if (!ops->validate_vbmeta_public_key(ops, pk_data, pk_len)) {
+    io_ret =
+        ops->validate_vbmeta_public_key(ops, pk_data, pk_len, &key_is_trusted);
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto out;
+    } else if (io_ret != AVB_IO_RESULT_OK) {
+      avb_errorv(full_partition_name,
+                 ": Error while checking public key used to sign data.\n",
+                 NULL);
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
+      goto out;
+    }
+    if (!key_is_trusted) {
       avb_errorv(full_partition_name,
                  ": Public key used to sign data rejected.\n", NULL);
       ret = AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED;
@@ -290,8 +313,12 @@
                                              &vbmeta_header);
 
   /* Check rollback index. */
-  if (!ops->read_rollback_index(ops, rollback_index_slot,
-                                &stored_rollback_index)) {
+  io_ret = ops->read_rollback_index(ops, rollback_index_slot,
+                                    &stored_rollback_index);
+  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+    goto out;
+  } else if (io_ret != AVB_IO_RESULT_OK) {
     avb_errorv(full_partition_name,
                ": Error getting rollback index for slot.\n", NULL);
     ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
@@ -480,17 +507,19 @@
   return ret;
 }
 
+#define NUM_GUIDS 2
+
 /* Substitutes all variables (e.g. $(ANDROID_SYSTEM_PARTUUID)) with
  * values. Returns NULL on OOM, otherwise the cmdline with values
  * replaced.
  */
 static char* sub_cmdline(AvbOps* ops, const char* cmdline,
                          const char* ab_suffix) {
-  const int NUM_GUIDS = 2;
   const char* part_name_str[NUM_GUIDS] = {"system", "boot"};
   const char* replace_str[NUM_GUIDS] = {"$(ANDROID_SYSTEM_PARTUUID)",
                                         "$(ANDROID_BOOT_PARTUUID)"};
   char* ret = NULL;
+  AvbIOResult io_ret;
 
   /* Replace unique partition GUIDs */
   for (size_t n = 0; n < NUM_GUIDS; n++) {
@@ -505,8 +534,11 @@
       goto fail;
     }
 
-    if (!ops->get_unique_guid_for_partition(ops, part_name, guid_buf,
-                                            sizeof guid_buf)) {
+    io_ret = ops->get_unique_guid_for_partition(ops, part_name, guid_buf,
+                                                sizeof guid_buf);
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      return NULL;
+    } else if (io_ret != AVB_IO_RESULT_OK) {
       avb_error("Error getting unique GUID for partition.\n");
       goto fail;
     }
@@ -615,6 +647,7 @@
   AvbSlotVerifyResult ret;
   AvbSlotVerifyData* slot_data = NULL;
   AvbAlgorithmType algorithm_type = AVB_ALGORITHM_TYPE_NONE;
+  AvbIOResult io_ret;
 
   if (out_data != NULL) {
     *out_data = NULL;
@@ -662,7 +695,11 @@
 
     /* Set androidboot.avb.device_state to "locked" or "unlocked". */
     bool is_device_unlocked;
-    if (!ops->read_is_device_unlocked(ops, &is_device_unlocked)) {
+    io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
+    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      goto fail;
+    } else if (io_ret != AVB_IO_RESULT_OK) {
       avb_error("Error getting device state.\n");
       ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
       goto fail;
diff --git a/test/avb_ab_flow_unittest.cc b/test/avb_ab_flow_unittest.cc
index fdfbb5f..09ec12c 100644
--- a/test/avb_ab_flow_unittest.cc
+++ b/test/avb_ab_flow_unittest.cc
@@ -202,7 +202,7 @@
     data.slots[1].priority = b_pri;
     data.slots[1].tries_remaining = b_tries;
     data.slots[1].successful_boot = (b_success ? 1 : 0);
-    EXPECT_TRUE(avb_ab_data_write(ops_.avb_ops(), &data));
+    EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_write(ops_.avb_ops(), &data));
     GenerateSlot(0, a_slot_valid, a_rollback_boot, a_rollback_odm);
     GenerateSlot(1, b_slot_valid, b_rollback_boot, b_rollback_odm);
     ops_.set_stored_rollback_indexes(stored_rollback_indexes);
@@ -211,19 +211,19 @@
   FakeAvbOps ops_;
 };
 
-#define ExpMD(a_pri, a_tries, a_success, b_pri, b_tries, b_success, \
-              stored_rollback_indexes)                              \
-  do {                                                              \
-    AvbABData data;                                                 \
-    EXPECT_TRUE(avb_ab_data_read(ops_.avb_ops(), &data));           \
-    EXPECT_EQ(a_pri, data.slots[0].priority);                       \
-    EXPECT_EQ(a_tries, data.slots[0].tries_remaining);              \
-    EXPECT_EQ(a_success ? 1 : 0, data.slots[0].successful_boot);    \
-    EXPECT_EQ(b_pri, data.slots[1].priority);                       \
-    EXPECT_EQ(b_tries, data.slots[1].tries_remaining);              \
-    EXPECT_EQ(b_success ? 1 : 0, data.slots[1].successful_boot);    \
-    EXPECT_EQ(std::vector<uint64_t>(stored_rollback_indexes),       \
-              ops_.get_stored_rollback_indexes());                  \
+#define ExpMD(a_pri, a_tries, a_success, b_pri, b_tries, b_success,       \
+              stored_rollback_indexes)                                    \
+  do {                                                                    \
+    AvbABData data;                                                       \
+    EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ops(), &data)); \
+    EXPECT_EQ(a_pri, data.slots[0].priority);                             \
+    EXPECT_EQ(a_tries, data.slots[0].tries_remaining);                    \
+    EXPECT_EQ(a_success ? 1 : 0, data.slots[0].successful_boot);          \
+    EXPECT_EQ(b_pri, data.slots[1].priority);                             \
+    EXPECT_EQ(b_tries, data.slots[1].tries_remaining);                    \
+    EXPECT_EQ(b_success ? 1 : 0, data.slots[1].successful_boot);          \
+    EXPECT_EQ(std::vector<uint64_t>(stored_rollback_indexes),             \
+              ops_.get_stored_rollback_indexes());                        \
   } while (0);
 
 TEST_F(AvbABFlowTest, MetadataReadAndWrite) {
@@ -232,7 +232,7 @@
 
   // First load from an uninitialized 'misc' partition. This should
   // not fail and just returned initialized data.
-  EXPECT_TRUE(avb_ab_data_read(ops_.avb_ops(), &loaded));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ops(), &loaded));
   EXPECT_EQ(AVB_AB_MAX_PRIORITY, loaded.slots[0].priority);
   EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, loaded.slots[0].tries_remaining);
   EXPECT_EQ(0, loaded.slots[0].successful_boot);
@@ -245,8 +245,8 @@
   avb_ab_data_init(&data);
   data.slots[0].priority = 2;
   data.slots[0].tries_remaining = 3;
-  EXPECT_TRUE(avb_ab_data_write(ops_.avb_ops(), &data));
-  EXPECT_TRUE(avb_ab_data_read(ops_.avb_ops(), &loaded));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_write(ops_.avb_ops(), &data));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ops(), &loaded));
   EXPECT_EQ(2, loaded.slots[0].priority);
   EXPECT_EQ(3, loaded.slots[0].tries_remaining);
 }
@@ -495,7 +495,7 @@
   SetMD(15, 0, 1, false, 0, 0,  // A: pri, tries, success, slot_valid, RIs
         11, 0, 1, true, 0, 0,   // B: pri, tries, success, slot_valid, RIs
         {0, 0});                // stored_rollback_indexes
-  EXPECT_TRUE(avb_ab_mark_slot_active(ops_.avb_ops(), 0));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_active(ops_.avb_ops(), 0));
   ExpMD(15, 7, 0,                        // A: pri, tries, successful
         11, 0, 1,                        // B: pri, tries, successful
         std::vector<uint64_t>({0, 0}));  // stored_rollback_indexes
@@ -505,7 +505,7 @@
   SetMD(15, 0, 1, false, 0, 0,  // A: pri, tries, success, slot_valid, RIs
         14, 0, 1, true, 0, 0,   // B: pri, tries, success, slot_valid, RIs
         {0, 0});                // stored_rollback_indexes
-  EXPECT_TRUE(avb_ab_mark_slot_active(ops_.avb_ops(), 1));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_active(ops_.avb_ops(), 1));
   ExpMD(14, 0, 1,                        // A: pri, tries, successful
         15, 7, 0,                        // B: pri, tries, successful
         std::vector<uint64_t>({0, 0}));  // stored_rollback_indexes
@@ -515,7 +515,7 @@
   SetMD(15, 0, 1, false, 0, 0,  // A: pri, tries, success, slot_valid, RIs
         11, 0, 1, true, 0, 0,   // B: pri, tries, success, slot_valid, RIs
         {0, 0});                // stored_rollback_indexes
-  EXPECT_TRUE(avb_ab_mark_slot_unbootable(ops_.avb_ops(), 0));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_unbootable(ops_.avb_ops(), 0));
   ExpMD(0, 0, 0,                         // A: pri, tries, successful
         11, 0, 1,                        // B: pri, tries, successful
         std::vector<uint64_t>({0, 0}));  // stored_rollback_indexes
@@ -523,7 +523,7 @@
   SetMD(15, 0, 1, false, 0, 0,  // A: pri, tries, success, slot_valid, RIs
         14, 0, 1, true, 0, 0,   // B: pri, tries, success, slot_valid, RIs
         {0, 0});                // stored_rollback_indexes
-  EXPECT_TRUE(avb_ab_mark_slot_unbootable(ops_.avb_ops(), 1));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_unbootable(ops_.avb_ops(), 1));
   ExpMD(15, 0, 1,                        // A: pri, tries, successful
         0, 0, 0,                         // B: pri, tries, successful
         std::vector<uint64_t>({0, 0}));  // stored_rollback_indexes
@@ -533,7 +533,7 @@
   SetMD(15, 5, 0, false, 0, 0,  // A: pri, tries, success, slot_valid, RIs
         11, 3, 0, true, 0, 0,   // B: pri, tries, success, slot_valid, RIs
         {0, 0});                // stored_rollback_indexes
-  EXPECT_TRUE(avb_ab_mark_slot_successful(ops_.avb_ops(), 0));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_successful(ops_.avb_ops(), 0));
   ExpMD(15, 0, 1,                        // A: pri, tries, successful
         11, 3, 0,                        // B: pri, tries, successful
         std::vector<uint64_t>({0, 0}));  // stored_rollback_indexes
@@ -541,17 +541,18 @@
   SetMD(15, 5, 0, false, 0, 0,  // A: pri, tries, success, slot_valid, RIs
         14, 0, 1, true, 0, 0,   // B: pri, tries, success, slot_valid, RIs
         {0, 0});                // stored_rollback_indexes
-  EXPECT_TRUE(avb_ab_mark_slot_successful(ops_.avb_ops(), 1));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_successful(ops_.avb_ops(), 1));
   ExpMD(15, 5, 0,                        // A: pri, tries, successful
         14, 0, 1,                        // B: pri, tries, successful
         std::vector<uint64_t>({0, 0}));  // stored_rollback_indexes
 
-  // Marking an unbootable slot (A) as successful fails. Also note how
-  // the unbootable slot is normalized in the process.
+  // Marking an unbootable slot (A) as successful won't work (it's a
+  // programmer error to do so)... notice however that the unbootable
+  // slot is normalized in the process.
   SetMD(0, 3, 2, false, 0, 0,  // A: pri, tries, success, slot_valid, RIs
         14, 0, 1, true, 0, 0,  // B: pri, tries, success, slot_valid, RIs
         {0, 0});               // stored_rollback_indexes
-  EXPECT_FALSE(avb_ab_mark_slot_successful(ops_.avb_ops(), 0));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_successful(ops_.avb_ops(), 0));
   ExpMD(0, 0, 0,                         // A: pri, tries, successful
         14, 0, 1,                        // B: pri, tries, successful
         std::vector<uint64_t>({0, 0}));  // stored_rollback_indexes
@@ -567,7 +568,7 @@
                  " --slot_data 13:3:0:11:2:1",
                  misc_path.value().c_str());
 
-  EXPECT_TRUE(avb_ab_data_read(ops_.avb_ops(), &data));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ops(), &data));
   EXPECT_EQ(13, data.slots[0].priority);
   EXPECT_EQ(3, data.slots[0].tries_remaining);
   EXPECT_EQ(0, data.slots[0].successful_boot);
@@ -588,7 +589,7 @@
                  " --slot_data 12:2:1:10:5:0",
                  misc_path.value().c_str());
 
-  EXPECT_TRUE(avb_ab_data_read(ops_.avb_ops(), &data));
+  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ops(), &data));
   EXPECT_EQ(12, data.slots[0].priority);
   EXPECT_EQ(2, data.slots[0].tries_remaining);
   EXPECT_EQ(1, data.slots[0].successful_boot);
diff --git a/test/fake_avb_ops.cc b/test/fake_avb_ops.cc
index 42bc133..193ab27 100644
--- a/test/fake_avb_ops.cc
+++ b/test/fake_avb_ops.cc
@@ -127,52 +127,56 @@
   return AVB_IO_RESULT_OK;
 }
 
-int FakeAvbOps::validate_vbmeta_public_key(AvbOps* ops,
-                                           const uint8_t* public_key_data,
-                                           size_t public_key_length) {
-  if (public_key_length != expected_public_key_.size()) return 0;
-
-  return memcmp(expected_public_key_.c_str(), public_key_data,
-                public_key_length) == 0;
+AvbIOResult FakeAvbOps::validate_vbmeta_public_key(
+    AvbOps* ops, const uint8_t* public_key_data, size_t public_key_length,
+    bool* out_key_is_trusted) {
+  if (out_key_is_trusted != NULL) {
+    *out_key_is_trusted = (public_key_length == expected_public_key_.size() &&
+                           (memcmp(expected_public_key_.c_str(),
+                                   public_key_data, public_key_length) == 0));
+  }
+  return AVB_IO_RESULT_OK;
 }
 
-bool FakeAvbOps::read_rollback_index(AvbOps* ops, size_t rollback_index_slot,
-                                     uint64_t* out_rollback_index) {
+AvbIOResult FakeAvbOps::read_rollback_index(AvbOps* ops,
+                                            size_t rollback_index_slot,
+                                            uint64_t* out_rollback_index) {
   if (rollback_index_slot >= stored_rollback_indexes_.size()) {
     fprintf(stderr, "No rollback index for slot %zd (has %zd slots).\n",
             rollback_index_slot, stored_rollback_indexes_.size());
-    return false;
+    return AVB_IO_RESULT_ERROR_IO;
   }
   *out_rollback_index = stored_rollback_indexes_[rollback_index_slot];
-  return true;
+  return AVB_IO_RESULT_OK;
 }
 
-bool FakeAvbOps::write_rollback_index(AvbOps* ops, size_t rollback_index_slot,
-                                      uint64_t rollback_index) {
+AvbIOResult FakeAvbOps::write_rollback_index(AvbOps* ops,
+                                             size_t rollback_index_slot,
+                                             uint64_t rollback_index) {
   if (rollback_index_slot >= stored_rollback_indexes_.size()) {
     fprintf(stderr, "No rollback index for slot %zd (has %zd slots).\n",
             rollback_index_slot, stored_rollback_indexes_.size());
-    return false;
+    return AVB_IO_RESULT_ERROR_IO;
   }
   stored_rollback_indexes_[rollback_index_slot] = rollback_index;
-  return true;
+  return AVB_IO_RESULT_OK;
 }
 
-bool FakeAvbOps::read_is_device_unlocked(AvbOps* ops,
-                                         bool* out_is_device_unlocked) {
+AvbIOResult FakeAvbOps::read_is_device_unlocked(AvbOps* ops,
+                                                bool* out_is_device_unlocked) {
   *out_is_device_unlocked = stored_is_device_unlocked_ ? 1 : 0;
-  return true;
+  return AVB_IO_RESULT_OK;
 }
 
-bool FakeAvbOps::get_unique_guid_for_partition(AvbOps* ops,
-                                               const char* partition,
-                                               char* guid_buf,
-                                               size_t guid_buf_size) {
+AvbIOResult FakeAvbOps::get_unique_guid_for_partition(AvbOps* ops,
+                                                      const char* partition,
+                                                      char* guid_buf,
+                                                      size_t guid_buf_size) {
   // This is faking it a bit but makes testing easy. It works
   // because avb_slot_verify.c doesn't check that the returned GUID
   // is wellformed.
   snprintf(guid_buf, guid_buf_size, "1234-fake-guid-for:%s", partition);
-  return true;
+  return AVB_IO_RESULT_OK;
 }
 
 struct FakeAvbOpsC {
@@ -197,37 +201,39 @@
       ->my_ops->write_to_partition(partition, offset, num_bytes, buffer);
 }
 
-static bool my_ops_validate_vbmeta_public_key(AvbOps* ops,
-                                              const uint8_t* public_key_data,
-                                              size_t public_key_length) {
+static AvbIOResult my_ops_validate_vbmeta_public_key(
+    AvbOps* ops, const uint8_t* public_key_data, size_t public_key_length,
+    bool* out_key_is_trusted) {
   return ((FakeAvbOpsC*)ops)
-      ->my_ops->validate_vbmeta_public_key(ops, public_key_data,
-                                           public_key_length);
+      ->my_ops->validate_vbmeta_public_key(
+          ops, public_key_data, public_key_length, out_key_is_trusted);
 }
 
-static bool my_ops_read_rollback_index(AvbOps* ops, size_t rollback_index_slot,
-                                       uint64_t* out_rollback_index) {
+static AvbIOResult my_ops_read_rollback_index(AvbOps* ops,
+                                              size_t rollback_index_slot,
+                                              uint64_t* out_rollback_index) {
   return ((FakeAvbOpsC*)ops)
       ->my_ops->read_rollback_index(ops, rollback_index_slot,
                                     out_rollback_index);
 }
 
-static bool my_ops_write_rollback_index(AvbOps* ops, size_t rollback_index_slot,
-                                        uint64_t rollback_index) {
+static AvbIOResult my_ops_write_rollback_index(AvbOps* ops,
+                                               size_t rollback_index_slot,
+                                               uint64_t rollback_index) {
   return ((FakeAvbOpsC*)ops)
       ->my_ops->write_rollback_index(ops, rollback_index_slot, rollback_index);
 }
 
-static bool my_ops_read_is_device_unlocked(AvbOps* ops,
-                                           bool* out_is_device_unlocked) {
+static AvbIOResult my_ops_read_is_device_unlocked(
+    AvbOps* ops, bool* out_is_device_unlocked) {
   return ((FakeAvbOpsC*)ops)
       ->my_ops->read_is_device_unlocked(ops, out_is_device_unlocked);
 }
 
-static bool my_ops_get_unique_guid_for_partition(AvbOps* ops,
-                                                 const char* partition,
-                                                 char* guid_buf,
-                                                 size_t guid_buf_size) {
+static AvbIOResult my_ops_get_unique_guid_for_partition(AvbOps* ops,
+                                                        const char* partition,
+                                                        char* guid_buf,
+                                                        size_t guid_buf_size) {
   return ((FakeAvbOpsC*)ops)
       ->my_ops->get_unique_guid_for_partition(ops, partition, guid_buf,
                                               guid_buf_size);
diff --git a/test/fake_avb_ops.h b/test/fake_avb_ops.h
index 8513cae..42a47d0 100644
--- a/test/fake_avb_ops.h
+++ b/test/fake_avb_ops.h
@@ -69,19 +69,23 @@
   AvbIOResult write_to_partition(const char* partition, int64_t offset,
                                  size_t num_bytes, const void* buffer);
 
-  int validate_vbmeta_public_key(AvbOps* ops, const uint8_t* public_key_data,
-                                 size_t public_key_length);
+  AvbIOResult validate_vbmeta_public_key(AvbOps* ops,
+                                         const uint8_t* public_key_data,
+                                         size_t public_key_length,
+                                         bool* out_key_is_trusted);
 
-  bool read_rollback_index(AvbOps* ops, size_t rollback_index_slot,
-                           uint64_t* out_rollback_index);
+  AvbIOResult read_rollback_index(AvbOps* ops, size_t rollback_index_slot,
+                                  uint64_t* out_rollback_index);
 
-  bool write_rollback_index(AvbOps* ops, size_t rollback_index_slot,
-                            uint64_t rollback_index);
+  AvbIOResult write_rollback_index(AvbOps* ops, size_t rollback_index_slot,
+                                   uint64_t rollback_index);
 
-  bool read_is_device_unlocked(AvbOps* ops, bool* out_is_device_unlocked);
+  AvbIOResult read_is_device_unlocked(AvbOps* ops,
+                                      bool* out_is_device_unlocked);
 
-  bool get_unique_guid_for_partition(AvbOps* ops, const char* partition,
-                                     char* guid_buf, size_t guid_buf_size);
+  AvbIOResult get_unique_guid_for_partition(AvbOps* ops, const char* partition,
+                                            char* guid_buf,
+                                            size_t guid_buf_size);
 
  private:
   FakeAvbOpsC* avb_ops_;