[U-Boot] [PATCH 1/2] libavb: Update libavb to current AOSP master

Igor Opaniuk igor.opaniuk at gmail.com
Wed Oct 16 09:32:30 UTC 2019


Hi Sam,

On Thu, Aug 15, 2019 at 11:04 PM Sam Protsenko
<semen.protsenko at linaro.org> wrote:
>
> Update libavb to commit 5fbb42a189aa in AOSP/master, because new version
> has support for super partition [1], which we need for implementing
> Android dynamic partitions. All changes from previous patches for libavb
> in U-Boot are accounted for in this commit:
>   - commit ecc6f6bea6a2 ("libavb: Handle wrong hashtree_error_mode in
>                           avb_append_options()")
>   - commit 897a1d947e7e ("libavb: Update SPDX tag style")
>   - commit d8f9d2af96b3 ("avb2.0: add Android Verified Boot 2.0 library")
>
> Tested on X15:
>
>     ## Android Verified Boot 2.0 version 1.1.0
>     read_is_device_unlocked not supported yet
>     read_rollback_index not supported yet
>     read_is_device_unlocked not supported yet
>     Verification passed successfully
>     AVB verification OK.
>
> Unit test passes:
>
>     $ ./test/py/test.py --bd sandbox --build -k test_avb
>
>       test/py/tests/test_android/test_avb.py ss..s.
>
> [1] https://android.googlesource.com/platform/external/avb/+/49936b4c0109411fdd38bd4ba3a32a01c40439a9
>
> Signed-off-by: Sam Protsenko <semen.protsenko at linaro.org>
> ---
>  lib/libavb/avb_cmdline.c       |  52 ++-
>  lib/libavb/avb_cmdline.h       |   4 +-
>  lib/libavb/avb_descriptor.c    |  11 +-
>  lib/libavb/avb_ops.h           |  41 ++-
>  lib/libavb/avb_sha.h           |  12 +-
>  lib/libavb/avb_sha256.c        |  36 +-
>  lib/libavb/avb_sha512.c        |  22 +-
>  lib/libavb/avb_slot_verify.c   | 652 +++++++++++++++++++++++++--------
>  lib/libavb/avb_slot_verify.h   |  59 ++-
>  lib/libavb/avb_sysdeps.h       |   8 +
>  lib/libavb/avb_sysdeps_posix.c |  16 +-
>  lib/libavb/avb_vbmeta_image.c  |  11 +-
>  12 files changed, 710 insertions(+), 214 deletions(-)
>
> diff --git a/lib/libavb/avb_cmdline.c b/lib/libavb/avb_cmdline.c
> index d246699272..cb5b98e423 100644
> --- a/lib/libavb/avb_cmdline.c
> +++ b/lib/libavb/avb_cmdline.c
> @@ -39,6 +39,14 @@ char* avb_sub_cmdline(AvbOps* ops,
>      char part_name[AVB_PART_NAME_MAX_SIZE];
>      char guid_buf[37];
>
> +    /* Don't attempt to query the partition guid unless its search string is
> +     * present in the command line. Note: the original cmdline is used here,
> +     * not the replaced one. See b/116010959.
> +     */
> +    if (avb_strstr(cmdline, replace_str[n]) == NULL) {
> +      continue;
> +    }
> +
>      if (!avb_str_concat(part_name,
>                          sizeof part_name,
>                          part_name_str[n],
> @@ -70,7 +78,15 @@ char* avb_sub_cmdline(AvbOps* ops,
>      }
>    }
>
> -  avb_assert(ret != NULL);
> +  /* It's possible there is no _PARTUUID for replacement above.
> +   * Duplicate cmdline to ret for additional substitutions below.
> +   */
> +  if (ret == NULL) {
> +    ret = avb_strdup(cmdline);
> +    if (ret == NULL) {
> +      goto fail;
> +    }
> +  }
>
>    /* Replace any additional substitutions. */
>    if (additional_substitutions != NULL) {
> @@ -198,21 +214,27 @@ static int cmdline_append_hex(AvbSlotVerifyData* slot_data,
>
>  AvbSlotVerifyResult avb_append_options(
>      AvbOps* ops,
> +    AvbSlotVerifyFlags flags,
>      AvbSlotVerifyData* slot_data,
>      AvbVBMetaImageHeader* toplevel_vbmeta,
>      AvbAlgorithmType algorithm_type,
> -    AvbHashtreeErrorMode hashtree_error_mode) {
> +    AvbHashtreeErrorMode hashtree_error_mode,
> +    AvbHashtreeErrorMode resolved_hashtree_error_mode) {
>    AvbSlotVerifyResult ret;
>    const char* verity_mode;
>    bool is_device_unlocked;
>    AvbIOResult io_ret;
>
> -  /* Add androidboot.vbmeta.device option. */
> -  if (!cmdline_append_option(slot_data,
> -                             "androidboot.vbmeta.device",
> -                             "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
> -    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> -    goto out;
> +  /* Add androidboot.vbmeta.device option... except if not using a vbmeta
> +   * partition since it doesn't make sense in that case.
> +   */
> +  if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
> +    if (!cmdline_append_option(slot_data,
> +                               "androidboot.vbmeta.device",
> +                               "PARTUUID=$(ANDROID_VBMETA_PARTUUID)")) {
> +      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> +      goto out;
> +    }
>    }
>
>    /* Add androidboot.vbmeta.avb_version option. */
> @@ -304,7 +326,7 @@ AvbSlotVerifyResult avb_append_options(
>      const char* dm_verity_mode;
>      char* new_ret;
>
> -    switch (hashtree_error_mode) {
> +    switch (resolved_hashtree_error_mode) {
>        case AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE:
>          if (!cmdline_append_option(
>                  slot_data, "androidboot.vbmeta.invalidate_on_error", "yes")) {
> @@ -331,6 +353,11 @@ AvbSlotVerifyResult avb_append_options(
>          verity_mode = "logging";
>          dm_verity_mode = "ignore_corruption";
>          break;
> +      case AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO:
> +        // Should never get here because MANAGED_RESTART_AND_EIO is
> +        // remapped by avb_manage_hashtree_error_mode().
> +        avb_assert_not_reached();
> +        break;
>        default:
>          ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
>          goto out;
> @@ -349,6 +376,13 @@ AvbSlotVerifyResult avb_append_options(
>      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
>      goto out;
>    }
> +  if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
> +    if (!cmdline_append_option(
> +            slot_data, "androidboot.veritymode.managed", "yes")) {
> +      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> +      goto out;
> +    }
> +  }
>
>    ret = AVB_SLOT_VERIFY_RESULT_OK;
>
> diff --git a/lib/libavb/avb_cmdline.h b/lib/libavb/avb_cmdline.h
> index 9af3a99994..96539d84bd 100644
> --- a/lib/libavb/avb_cmdline.h
> +++ b/lib/libavb/avb_cmdline.h
> @@ -43,10 +43,12 @@ char* avb_sub_cmdline(AvbOps* ops,
>
>  AvbSlotVerifyResult avb_append_options(
>      AvbOps* ops,
> +    AvbSlotVerifyFlags flags,
>      AvbSlotVerifyData* slot_data,
>      AvbVBMetaImageHeader* toplevel_vbmeta,
>      AvbAlgorithmType algorithm_type,
> -    AvbHashtreeErrorMode hashtree_error_mode);
> +    AvbHashtreeErrorMode hashtree_error_mode,
> +    AvbHashtreeErrorMode resolved_hashtree_error_mode);
>
>  /* Allocates and initializes a new command line substitution list. Free with
>   * |avb_free_cmdline_subst_list|.
> diff --git a/lib/libavb/avb_descriptor.c b/lib/libavb/avb_descriptor.c
> index fb0b305f2c..9f03b9777a 100644
> --- a/lib/libavb/avb_descriptor.c
> +++ b/lib/libavb/avb_descriptor.c
> @@ -72,7 +72,11 @@ bool avb_descriptor_foreach(const uint8_t* image_data,
>      const AvbDescriptor* dh = (const AvbDescriptor*)p;
>      avb_assert_aligned(dh);
>      uint64_t nb_following = avb_be64toh(dh->num_bytes_following);
> -    uint64_t nb_total = sizeof(AvbDescriptor) + nb_following;
> +    uint64_t nb_total = 0;
> +    if (!avb_safe_add(&nb_total, sizeof(AvbDescriptor), nb_following)) {
> +      avb_error("Invalid descriptor length.\n");
> +      goto out;
> +    }
>
>      if ((nb_total & 7) != 0) {
>        avb_error("Invalid descriptor length.\n");
> @@ -88,7 +92,10 @@ bool avb_descriptor_foreach(const uint8_t* image_data,
>        goto out;
>      }
>
> -    p += nb_total;
> +    if (!avb_safe_add_to((uint64_t*)(&p), nb_total)) {
> +      avb_error("Invalid descriptor length.\n");
> +      goto out;
> +    }
>    }
>
>    ret = true;
> diff --git a/lib/libavb/avb_ops.h b/lib/libavb/avb_ops.h
> index 8bbdc7c31b..6a5c589da8 100644
> --- a/lib/libavb/avb_ops.h
> +++ b/lib/libavb/avb_ops.h
> @@ -18,6 +18,7 @@ extern "C" {
>
>  /* Well-known names of named persistent values. */
>  #define AVB_NPV_PERSISTENT_DIGEST_PREFIX "avb.persistent_digest."
> +#define AVB_NPV_MANAGED_VERITY_MODE "avb.managed_verity_mode"
>
>  /* Return codes used for I/O operations.
>   *
> @@ -171,6 +172,10 @@ struct AvbOps {
>     *
>     * If AVB_IO_RESULT_OK is returned then |out_is_trusted| is set -
>     * true if trusted or false if untrusted.
> +   *
> +   * NOTE: If AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is passed to
> +   * avb_slot_verify() then this operation is never used. Instead, the
> +   * validate_public_key_for_partition() operation is used
>     */
>    AvbIOResult (*validate_vbmeta_public_key)(AvbOps* ops,
>                                              const uint8_t* public_key_data,
> @@ -231,6 +236,9 @@ struct AvbOps {
>     * (NUL-terminated UTF-8 string). Returns the value in
>     * |out_size_num_bytes|.
>     *
> +   * If the partition doesn't exist the AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION
> +   * error code should be returned.
> +   *
>     * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
>     */
>    AvbIOResult (*get_size_of_partition)(AvbOps* ops,
> @@ -253,9 +261,10 @@ struct AvbOps {
>     * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If |buffer_size| is smaller than the
>     * size of the stored value, returns AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE.
>     *
> -   * This operation is currently only used to support persistent digests. If a
> -   * device does not use persistent digests this function pointer can be set to
> -   * NULL.
> +   * This operation is currently only used to support persistent digests or the
> +   * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO hashtree error mode. If a
> +   * device does not use one of these features this function pointer can be set
> +   * to NULL.
>     */
>    AvbIOResult (*read_persistent_value)(AvbOps* ops,
>                                         const char* name,
> @@ -275,14 +284,34 @@ struct AvbOps {
>     * AVB_IO_RESULT_ERROR_NO_SUCH_VALUE. If the |value_size| is not supported,
>     * returns AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE.
>     *
> -   * This operation is currently only used to support persistent digests. If a
> -   * device does not use persistent digests this function pointer can be set to
> -   * NULL.
> +   * This operation is currently only used to support persistent digests or the
> +   * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO hashtree error mode. If a
> +   * device does not use one of these features this function pointer can be set
> +   * to NULL.
>     */
>    AvbIOResult (*write_persistent_value)(AvbOps* ops,
>                                          const char* name,
>                                          size_t value_size,
>                                          const uint8_t* value);
> +
> +  /* Like validate_vbmeta_public_key() but for when the flag
> +   * AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is being used. The name of the
> +   * partition to get the public key for is passed in |partition_name|.
> +   *
> +   * Also returns the rollback index location to use for the partition, in
> +   * |out_rollback_index_location|.
> +   *
> +   * Returns AVB_IO_RESULT_OK on success, otherwise an error code.
> +   */
> +  AvbIOResult (*validate_public_key_for_partition)(
> +      AvbOps* ops,
> +      const char* partition,
> +      const uint8_t* public_key_data,
> +      size_t public_key_length,
> +      const uint8_t* public_key_metadata,
> +      size_t public_key_metadata_length,
> +      bool* out_is_trusted,
> +      uint32_t* out_rollback_index_location);
>  };
>
>  #ifdef __cplusplus
> diff --git a/lib/libavb/avb_sha.h b/lib/libavb/avb_sha.h
> index 365aaadc2f..f5d02e09f2 100644
> --- a/lib/libavb/avb_sha.h
> +++ b/lib/libavb/avb_sha.h
> @@ -31,8 +31,8 @@ extern "C" {
>  /* Data structure used for SHA-256. */
>  typedef struct {
>    uint32_t h[8];
> -  uint32_t tot_len;
> -  uint32_t len;
> +  uint64_t tot_len;
> +  size_t len;
>    uint8_t block[2 * AVB_SHA256_BLOCK_SIZE];
>    uint8_t buf[AVB_SHA256_DIGEST_SIZE]; /* Used for storing the final digest. */
>  } AvbSHA256Ctx;
> @@ -40,8 +40,8 @@ typedef struct {
>  /* Data structure used for SHA-512. */
>  typedef struct {
>    uint64_t h[8];
> -  uint32_t tot_len;
> -  uint32_t len;
> +  uint64_t tot_len;
> +  size_t len;
>    uint8_t block[2 * AVB_SHA512_BLOCK_SIZE];
>    uint8_t buf[AVB_SHA512_DIGEST_SIZE]; /* Used for storing the final digest. */
>  } AvbSHA512Ctx;
> @@ -50,7 +50,7 @@ typedef struct {
>  void avb_sha256_init(AvbSHA256Ctx* ctx);
>
>  /* Updates the SHA-256 context with |len| bytes from |data|. */
> -void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len);
> +void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, size_t len);
>
>  /* Returns the SHA-256 digest. */
>  uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
> @@ -59,7 +59,7 @@ uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
>  void avb_sha512_init(AvbSHA512Ctx* ctx);
>
>  /* Updates the SHA-512 context with |len| bytes from |data|. */
> -void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len);
> +void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, size_t len);
>
>  /* Returns the SHA-512 digest. */
>  uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) AVB_ATTR_WARN_UNUSED_RESULT;
> diff --git a/lib/libavb/avb_sha256.c b/lib/libavb/avb_sha256.c
> index d24c7015f6..86ecca57b7 100644
> --- a/lib/libavb/avb_sha256.c
> +++ b/lib/libavb/avb_sha256.c
> @@ -29,6 +29,18 @@
>      *((str) + 0) = (uint8_t)((x) >> 24); \
>    }
>
> +#define UNPACK64(x, str)                         \
> +  {                                              \
> +    *((str) + 7) = (uint8_t)x;                   \
> +    *((str) + 6) = (uint8_t)((uint64_t)x >> 8);  \
> +    *((str) + 5) = (uint8_t)((uint64_t)x >> 16); \
> +    *((str) + 4) = (uint8_t)((uint64_t)x >> 24); \
> +    *((str) + 3) = (uint8_t)((uint64_t)x >> 32); \
> +    *((str) + 2) = (uint8_t)((uint64_t)x >> 40); \
> +    *((str) + 1) = (uint8_t)((uint64_t)x >> 48); \
> +    *((str) + 0) = (uint8_t)((uint64_t)x >> 56); \
> +  }
> +
>  #define PACK32(str, x)                                                    \
>    {                                                                       \
>      *(x) = ((uint32_t) * ((str) + 3)) | ((uint32_t) * ((str) + 2) << 8) | \
> @@ -96,18 +108,18 @@ void avb_sha256_init(AvbSHA256Ctx* ctx) {
>
>  static void SHA256_transform(AvbSHA256Ctx* ctx,
>                               const uint8_t* message,
> -                             unsigned int block_nb) {
> +                             size_t block_nb) {
>    uint32_t w[64];
>    uint32_t wv[8];
>    uint32_t t1, t2;
>    const unsigned char* sub_block;
> -  int i;
> +  size_t i;
>
>  #ifndef UNROLL_LOOPS
> -  int j;
> +  size_t j;
>  #endif
>
> -  for (i = 0; i < (int)block_nb; i++) {
> +  for (i = 0; i < block_nb; i++) {
>      sub_block = message + (i << 6);
>
>  #ifndef UNROLL_LOOPS
> @@ -293,9 +305,9 @@ static void SHA256_transform(AvbSHA256Ctx* ctx,
>    }
>  }
>
> -void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len) {
> -  unsigned int block_nb;
> -  unsigned int new_len, rem_len, tmp_len;
> +void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, size_t len) {
> +  size_t block_nb;
> +  size_t new_len, rem_len, tmp_len;
>    const uint8_t* shifted_data;
>
>    tmp_len = AVB_SHA256_BLOCK_SIZE - ctx->len;
> @@ -325,11 +337,11 @@ void avb_sha256_update(AvbSHA256Ctx* ctx, const uint8_t* data, uint32_t len) {
>  }
>
>  uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) {
> -  unsigned int block_nb;
> -  unsigned int pm_len;
> -  unsigned int len_b;
> +  size_t block_nb;
> +  size_t pm_len;
> +  uint64_t len_b;
>  #ifndef UNROLL_LOOPS
> -  int i;
> +  size_t i;
>  #endif
>
>    block_nb =
> @@ -340,7 +352,7 @@ uint8_t* avb_sha256_final(AvbSHA256Ctx* ctx) {
>
>    avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
>    ctx->block[ctx->len] = 0x80;
> -  UNPACK32(len_b, ctx->block + pm_len - 4);
> +  UNPACK64(len_b, ctx->block + pm_len - 8);
>
>    SHA256_transform(ctx, ctx->block, block_nb);
>
> diff --git a/lib/libavb/avb_sha512.c b/lib/libavb/avb_sha512.c
> index a5e7297aa7..b19054fc74 100644
> --- a/lib/libavb/avb_sha512.c
> +++ b/lib/libavb/avb_sha512.c
> @@ -127,14 +127,14 @@ void avb_sha512_init(AvbSHA512Ctx* ctx) {
>
>  static void SHA512_transform(AvbSHA512Ctx* ctx,
>                               const uint8_t* message,
> -                             unsigned int block_nb) {
> +                             size_t block_nb) {
>    uint64_t w[80];
>    uint64_t wv[8];
>    uint64_t t1, t2;
>    const uint8_t* sub_block;
> -  int i, j;
> +  size_t i, j;
>
> -  for (i = 0; i < (int)block_nb; i++) {
> +  for (i = 0; i < block_nb; i++) {
>      sub_block = message + (i << 7);
>
>  #ifdef UNROLL_LOOPS_SHA512
> @@ -291,9 +291,9 @@ static void SHA512_transform(AvbSHA512Ctx* ctx,
>    }
>  }
>
> -void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len) {
> -  unsigned int block_nb;
> -  unsigned int new_len, rem_len, tmp_len;
> +void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, size_t len) {
> +  size_t block_nb;
> +  size_t new_len, rem_len, tmp_len;
>    const uint8_t* shifted_data;
>
>    tmp_len = AVB_SHA512_BLOCK_SIZE - ctx->len;
> @@ -323,12 +323,12 @@ void avb_sha512_update(AvbSHA512Ctx* ctx, const uint8_t* data, uint32_t len) {
>  }
>
>  uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) {
> -  unsigned int block_nb;
> -  unsigned int pm_len;
> -  unsigned int len_b;
> +  size_t block_nb;
> +  size_t pm_len;
> +  uint64_t len_b;
>
>  #ifndef UNROLL_LOOPS_SHA512
> -  int i;
> +  size_t i;
>  #endif
>
>    block_nb =
> @@ -339,7 +339,7 @@ uint8_t* avb_sha512_final(AvbSHA512Ctx* ctx) {
>
>    avb_memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
>    ctx->block[ctx->len] = 0x80;
> -  UNPACK32(len_b, ctx->block + pm_len - 4);
> +  UNPACK64(len_b, ctx->block + pm_len - 8);
>
>    SHA512_transform(ctx, ctx->block, block_nb);
>
> diff --git a/lib/libavb/avb_slot_verify.c b/lib/libavb/avb_slot_verify.c
> index a941850d93..5d400b38aa 100644
> --- a/lib/libavb/avb_slot_verify.c
> +++ b/lib/libavb/avb_slot_verify.c
> @@ -24,6 +24,14 @@
>  /* Maximum size of a vbmeta image - 64 KiB. */
>  #define VBMETA_MAX_SIZE (64 * 1024)
>
> +static AvbSlotVerifyResult initialize_persistent_digest(
> +    AvbOps* ops,
> +    const char* part_name,
> +    const char* persistent_value_name,
> +    size_t digest_size,
> +    const uint8_t* initial_digest,
> +    uint8_t* out_digest);
> +
>  /* Helper function to see if we should continue with verification in
>   * allow_verification_error=true mode if something goes wrong. See the
>   * comments for the avb_slot_verify() function for more information.
> @@ -114,9 +122,26 @@ static AvbSlotVerifyResult load_full_partition(AvbOps* ops,
>    return AVB_SLOT_VERIFY_RESULT_OK;
>  }
>
> +/* Reads a persistent digest stored as a named persistent value corresponding to
> + * the given |part_name|. The value is returned in |out_digest| which must point
> + * to |expected_digest_size| bytes. If there is no digest stored for |part_name|
> + * it can be initialized by providing a non-NULL |initial_digest| of length
> + * |expected_digest_size|. This automatic initialization will only occur if the
> + * device is currently locked. The |initial_digest| may be NULL.
> + *
> + * Returns AVB_SLOT_VERIFY_RESULT_OK on success, otherwise returns an
> + * AVB_SLOT_VERIFY_RESULT_ERROR_* error code.
> + *
> + * If the value does not exist, is not supported, or is not populated, and
> + * |initial_digest| is NULL, returns
> + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA. If |expected_digest_size| does
> + * not match the stored digest size, also returns
> + * AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA.
> + */
>  static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops,
>                                                    const char* part_name,
>                                                    size_t expected_digest_size,
> +                                                  const uint8_t* initial_digest,
>                                                    uint8_t* out_digest) {
>    char* persistent_value_name = NULL;
>    AvbIOResult io_ret = AVB_IO_RESULT_OK;
> @@ -131,30 +156,106 @@ static AvbSlotVerifyResult read_persistent_digest(AvbOps* ops,
>    if (persistent_value_name == NULL) {
>      return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
>    }
> +
>    io_ret = ops->read_persistent_value(ops,
>                                        persistent_value_name,
>                                        expected_digest_size,
>                                        out_digest,
>                                        &stored_digest_size);
> +
> +  // If no such named persistent value exists and an initial digest value was
> +  // given, initialize the named persistent value with the given digest. If
> +  // initialized successfully, this will recurse into this function but with a
> +  // NULL initial_digest.
> +  if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE && initial_digest) {
> +    AvbSlotVerifyResult ret =
> +        initialize_persistent_digest(ops,
> +                                     part_name,
> +                                     persistent_value_name,
> +                                     expected_digest_size,
> +                                     initial_digest,
> +                                     out_digest);
> +    avb_free(persistent_value_name);
> +    return ret;
> +  }
>    avb_free(persistent_value_name);
> +
>    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
>      return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
>    } else if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE) {
> +    // Treat a missing persistent value as a verification error, which is
> +    // ignoreable, rather than a metadata error which is not.
>      avb_errorv(part_name, ": Persistent digest does not exist.\n", NULL);
> -    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
> +    return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
>    } else if (io_ret == AVB_IO_RESULT_ERROR_INVALID_VALUE_SIZE ||
> -             io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE ||
> -             expected_digest_size != stored_digest_size) {
> +             io_ret == AVB_IO_RESULT_ERROR_INSUFFICIENT_SPACE) {
>      avb_errorv(
>          part_name, ": Persistent digest is not of expected size.\n", NULL);
>      return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
>    } else if (io_ret != AVB_IO_RESULT_OK) {
>      avb_errorv(part_name, ": Error reading persistent digest.\n", NULL);
>      return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
> +  } else if (expected_digest_size != stored_digest_size) {
> +    avb_errorv(
> +        part_name, ": Persistent digest is not of expected size.\n", NULL);
> +    return AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
>    }
>    return AVB_SLOT_VERIFY_RESULT_OK;
>  }
>
> +static AvbSlotVerifyResult initialize_persistent_digest(
> +    AvbOps* ops,
> +    const char* part_name,
> +    const char* persistent_value_name,
> +    size_t digest_size,
> +    const uint8_t* initial_digest,
> +    uint8_t* out_digest) {
> +  AvbSlotVerifyResult ret;
> +  AvbIOResult io_ret = AVB_IO_RESULT_OK;
> +  bool is_device_unlocked = true;
> +
> +  io_ret = ops->read_is_device_unlocked(ops, &is_device_unlocked);
> +  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
> +    return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> +  } else if (io_ret != AVB_IO_RESULT_OK) {
> +    avb_error("Error getting device lock state.\n");
> +    return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
> +  }
> +
> +  if (is_device_unlocked) {
> +    avb_debugv(part_name,
> +               ": Digest does not exist, device unlocked so not initializing "
> +               "digest.\n",
> +               NULL);
> +    return AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION;
> +  }
> +
> +  // Device locked; initialize digest with given initial value.
> +  avb_debugv(part_name,
> +             ": Digest does not exist, initializing persistent digest.\n",
> +             NULL);
> +  io_ret = ops->write_persistent_value(
> +      ops, persistent_value_name, digest_size, initial_digest);
> +  if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
> +    return AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> +  } else if (io_ret != AVB_IO_RESULT_OK) {
> +    avb_errorv(part_name, ": Error initializing persistent digest.\n", NULL);
> +    return AVB_SLOT_VERIFY_RESULT_ERROR_IO;
> +  }
> +
> +  // To ensure that the digest value was written successfully - and avoid a
> +  // scenario where the digest is simply 'initialized' on every verify - recurse
> +  // into read_persistent_digest to read back the written value. The NULL
> +  // initial_digest ensures that this will not recurse again.
> +  ret = read_persistent_digest(ops, part_name, digest_size, NULL, out_digest);
> +  if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
> +    avb_errorv(part_name,
> +               ": Reading back initialized persistent digest failed!\n",
> +               NULL);
> +  }
> +  return ret;
> +}
> +
>  static AvbSlotVerifyResult load_and_verify_hash_partition(
>      AvbOps* ops,
>      const char* const* requested_partitions,
> @@ -248,24 +349,16 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
>     */
>    image_size = hash_desc.image_size;
>    if (allow_verification_error) {
> -    if (ops->get_size_of_partition == NULL) {
> -      avb_errorv(part_name,
> -                 ": The get_size_of_partition() operation is "
> -                 "not implemented so we may not load the entire partition. "
> -                 "Please implement.",
> -                 NULL);
> -    } else {
> -      io_ret = ops->get_size_of_partition(ops, part_name, &image_size);
> -      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 determining partition size.\n", NULL);
> -        ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
> -        goto out;
> -      }
> -      avb_debugv(part_name, ": Loading entire partition.\n", NULL);
> +    io_ret = ops->get_size_of_partition(ops, part_name, &image_size);
> +    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 determining partition size.\n", NULL);
> +      ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
> +      goto out;
>      }
> +    avb_debugv(part_name, ": Loading entire partition.\n", NULL);
>    }
>
>    ret = load_full_partition(
> @@ -273,19 +366,27 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
>    if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
>      goto out;
>    }
> -
> +  // Although only one of the type might be used, we have to defined the
> +  // structure here so that they would live outside the 'if/else' scope to be
> +  // used later.
> +  AvbSHA256Ctx sha256_ctx;
> +  AvbSHA512Ctx sha512_ctx;
> +  size_t image_size_to_hash = hash_desc.image_size;
> +  // If we allow verification error and the whole partition is smaller than
> +  // image size in hash descriptor, we just hash the whole partition.
> +  if (image_size_to_hash > image_size) {
> +    image_size_to_hash = image_size;
> +  }
>    if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha256") == 0) {
> -    AvbSHA256Ctx sha256_ctx;
>      avb_sha256_init(&sha256_ctx);
>      avb_sha256_update(&sha256_ctx, desc_salt, hash_desc.salt_len);
> -    avb_sha256_update(&sha256_ctx, image_buf, hash_desc.image_size);
> +    avb_sha256_update(&sha256_ctx, image_buf, image_size_to_hash);
>      digest = avb_sha256_final(&sha256_ctx);
>      digest_len = AVB_SHA256_DIGEST_SIZE;
>    } else if (avb_strcmp((const char*)hash_desc.hash_algorithm, "sha512") == 0) {
> -    AvbSHA512Ctx sha512_ctx;
>      avb_sha512_init(&sha512_ctx);
>      avb_sha512_update(&sha512_ctx, desc_salt, hash_desc.salt_len);
> -    avb_sha512_update(&sha512_ctx, image_buf, hash_desc.image_size);
> +    avb_sha512_update(&sha512_ctx, image_buf, image_size_to_hash);
>      digest = avb_sha512_final(&sha512_ctx);
>      digest_len = AVB_SHA512_DIGEST_SIZE;
>    } else {
> @@ -295,18 +396,21 @@ static AvbSlotVerifyResult load_and_verify_hash_partition(
>    }
>
>    if (hash_desc.digest_len == 0) {
> -    // Expect a match to a persistent digest.
> +    /* Expect a match to a persistent digest. */
>      avb_debugv(part_name, ": No digest, using persistent digest.\n", NULL);
>      expected_digest_len = digest_len;
>      expected_digest = expected_digest_buf;
>      avb_assert(expected_digest_len <= sizeof(expected_digest_buf));
> -    ret =
> -        read_persistent_digest(ops, part_name, digest_len, expected_digest_buf);
> +    /* Pass |digest| as the |initial_digest| so devices not yet initialized get
> +     * initialized to the current partition digest.
> +     */
> +    ret = read_persistent_digest(
> +        ops, part_name, digest_len, digest, expected_digest_buf);
>      if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
>        goto out;
>      }
>    } else {
> -    // Expect a match to the digest in the descriptor.
> +    /* Expect a match to the digest in the descriptor. */
>      expected_digest_len = hash_desc.digest_len;
>      expected_digest = desc_digest;
>    }
> @@ -365,12 +469,6 @@ static AvbSlotVerifyResult load_requested_partitions(
>    bool image_preloaded = false;
>    size_t n;
>
> -  if (ops->get_size_of_partition == NULL) {
> -    avb_error("get_size_of_partition() not implemented.\n");
> -    ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
> -    goto out;
> -  }
> -
>    for (n = 0; requested_partitions[n] != NULL; n++) {
>      char part_name[AVB_PART_NAME_MAX_SIZE];
>      AvbIOResult io_ret;
> @@ -441,6 +539,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>      AvbOps* ops,
>      const char* const* requested_partitions,
>      const char* ab_suffix,
> +    AvbSlotVerifyFlags flags,
>      bool allow_verification_error,
>      AvbVBMetaImageFlags toplevel_vbmeta_flags,
>      int rollback_index_location,
> @@ -467,7 +566,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>    size_t num_descriptors;
>    size_t n;
>    bool is_main_vbmeta;
> -  bool is_vbmeta_partition;
> +  bool look_for_vbmeta_footer;
>    AvbVBMetaData* vbmeta_image_data = NULL;
>
>    ret = AVB_SLOT_VERIFY_RESULT_OK;
> @@ -478,8 +577,20 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>     * rollback_index_location to determine whether we're the main
>     * vbmeta struct.
>     */
> -  is_main_vbmeta = (rollback_index_location == 0);
> -  is_vbmeta_partition = (avb_strcmp(partition_name, "vbmeta") == 0);
> +  is_main_vbmeta = false;
> +  if (rollback_index_location == 0) {
> +    if ((flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) == 0) {
> +      is_main_vbmeta = true;
> +    }
> +  }
> +
> +  /* Don't use footers for vbmeta partitions ('vbmeta' or
> +   * 'vbmeta_<partition_name>').
> +   */
> +  look_for_vbmeta_footer = true;
> +  if (avb_strncmp(partition_name, "vbmeta", avb_strlen("vbmeta")) == 0) {
> +    look_for_vbmeta_footer = false;
> +  }
>
>    if (!avb_validate_utf8((const uint8_t*)partition_name, partition_name_len)) {
>      avb_error("Partition name is not valid UTF-8.\n");
> @@ -487,7 +598,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>      goto out;
>    }
>
> -  /* Construct full partition name. */
> +  /* Construct full partition name e.g. system_a. */
>    if (!avb_str_concat(full_partition_name,
>                        sizeof full_partition_name,
>                        partition_name,
> @@ -499,19 +610,15 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>      goto out;
>    }
>
> -  avb_debugv("Loading vbmeta struct from partition '",
> -             full_partition_name,
> -             "'.\n",
> -             NULL);
> -
> -  /* If we're loading from the main vbmeta partition, the vbmeta
> -   * struct is in the beginning. Otherwise we have to locate it via a
> -   * footer.
> +  /* If we're loading from the main vbmeta partition, the vbmeta struct is in
> +   * the beginning. Otherwise we may have to locate it via a footer... if no
> +   * footer is found, we look in the beginning to support e.g. vbmeta_<org>
> +   * partitions holding data for e.g. super partitions (b/80195851 for
> +   * rationale).
>     */
> -  if (is_vbmeta_partition) {
> -    vbmeta_offset = 0;
> -    vbmeta_size = VBMETA_MAX_SIZE;
> -  } else {
> +  vbmeta_offset = 0;
> +  vbmeta_size = VBMETA_MAX_SIZE;
> +  if (look_for_vbmeta_footer) {
>      uint8_t footer_buf[AVB_FOOTER_SIZE];
>      size_t footer_num_read;
>      AvbFooter footer;
> @@ -534,21 +641,17 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>
>      if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf,
>                                            &footer)) {
> -      avb_errorv(full_partition_name, ": Error validating footer.\n", NULL);
> -      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
> -      goto out;
> -    }
> -
> -    /* Basic footer sanity check since the data is untrusted. */
> -    if (footer.vbmeta_size > VBMETA_MAX_SIZE) {
> -      avb_errorv(
> -          full_partition_name, ": Invalid vbmeta size in footer.\n", NULL);
> -      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
> -      goto out;
> +      avb_debugv(full_partition_name, ": No footer detected.\n", NULL);
> +    } else {
> +      /* Basic footer sanity check since the data is untrusted. */
> +      if (footer.vbmeta_size > VBMETA_MAX_SIZE) {
> +        avb_errorv(
> +            full_partition_name, ": Invalid vbmeta size in footer.\n", NULL);
> +      } else {
> +        vbmeta_offset = footer.vbmeta_offset;
> +        vbmeta_size = footer.vbmeta_size;
> +      }
>      }
> -
> -    vbmeta_offset = footer.vbmeta_offset;
> -    vbmeta_size = footer.vbmeta_size;
>    }
>
>    vbmeta_buf = avb_malloc(vbmeta_size);
> @@ -557,6 +660,18 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>      goto out;
>    }
>
> +  if (vbmeta_offset != 0) {
> +    avb_debugv("Loading vbmeta struct in footer from partition '",
> +               full_partition_name,
> +               "'.\n",
> +               NULL);
> +  } else {
> +    avb_debugv("Loading vbmeta struct from partition '",
> +               full_partition_name,
> +               "'.\n",
> +               NULL);
> +  }
> +
>    io_ret = ops->read_from_partition(ops,
>                                      full_partition_name,
>                                      vbmeta_offset,
> @@ -571,13 +686,14 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>       * go try to get it from the boot partition instead.
>       */
>      if (is_main_vbmeta && io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION &&
> -        is_vbmeta_partition) {
> +        !look_for_vbmeta_footer) {
>        avb_debugv(full_partition_name,
>                   ": No such partition. Trying 'boot' instead.\n",
>                   NULL);
>        ret = load_and_verify_vbmeta(ops,
>                                     requested_partitions,
>                                     ab_suffix,
> +                                   flags,
>                                     allow_verification_error,
>                                     0 /* toplevel_vbmeta_flags */,
>                                     0 /* rollback_index_location */,
> @@ -655,6 +771,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>      }
>    }
>
> +  uint32_t rollback_index_location_to_use = rollback_index_location;
> +
>    /* Check if key used to make signature matches what is expected. */
>    if (pk_data != NULL) {
>      if (expected_public_key != NULL) {
> @@ -682,9 +800,27 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>          pk_metadata_len = vbmeta_header.public_key_metadata_size;
>        }
>
> -      avb_assert(is_main_vbmeta);
> -      io_ret = ops->validate_vbmeta_public_key(
> -          ops, pk_data, pk_len, pk_metadata, pk_metadata_len, &key_is_trusted);
> +      // If we're not using a vbmeta partition, need to use another AvbOps...
> +      if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) {
> +        io_ret = ops->validate_public_key_for_partition(
> +            ops,
> +            full_partition_name,
> +            pk_data,
> +            pk_len,
> +            pk_metadata,
> +            pk_metadata_len,
> +            &key_is_trusted,
> +            &rollback_index_location_to_use);
> +      } else {
> +        avb_assert(is_main_vbmeta);
> +        io_ret = ops->validate_vbmeta_public_key(ops,
> +                                                 pk_data,
> +                                                 pk_len,
> +                                                 pk_metadata,
> +                                                 pk_metadata_len,
> +                                                 &key_is_trusted);
> +      }
> +
>        if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
>          ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
>          goto out;
> @@ -709,7 +845,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>
>    /* Check rollback index. */
>    io_ret = ops->read_rollback_index(
> -      ops, rollback_index_location, &stored_rollback_index);
> +      ops, rollback_index_location_to_use, &stored_rollback_index);
>    if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
>      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
>      goto out;
> @@ -735,7 +871,9 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>    if (is_main_vbmeta) {
>      avb_assert(slot_data->num_vbmeta_images == 0);
>    } else {
> -    avb_assert(slot_data->num_vbmeta_images > 0);
> +    if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
> +      avb_assert(slot_data->num_vbmeta_images > 0);
> +    }
>    }
>    if (slot_data->num_vbmeta_images == MAX_NUMBER_OF_VBMETA_IMAGES) {
>      avb_errorv(full_partition_name, ": Too many vbmeta images.\n", NULL);
> @@ -859,6 +997,7 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>              load_and_verify_vbmeta(ops,
>                                     requested_partitions,
>                                     ab_suffix,
> +                                   flags,
>                                     allow_verification_error,
>                                     toplevel_vbmeta_flags,
>                                     chain_desc.rollback_index_location,
> @@ -1019,7 +1158,11 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>              goto out;
>            }
>
> -          ret = read_persistent_digest(ops, part_name, digest_len, digest_buf);
> +          ret = read_persistent_digest(ops,
> +                                       part_name,
> +                                       digest_len,
> +                                       NULL /* initial_digest */,
> +                                       digest_buf);
>            if (ret != AVB_SLOT_VERIFY_RESULT_OK) {
>              goto out;
>            }
> @@ -1043,7 +1186,8 @@ static AvbSlotVerifyResult load_and_verify_vbmeta(
>      }
>    }
>
> -  if (rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) {
> +  if (rollback_index_location < 0 ||
> +      rollback_index_location >= AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) {
>      avb_errorv(
>          full_partition_name, ": Invalid rollback_index_location.\n", NULL);
>      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA;
> @@ -1072,6 +1216,130 @@ out:
>    return ret;
>  }
>
> +static AvbIOResult avb_manage_hashtree_error_mode(
> +    AvbOps* ops,
> +    AvbSlotVerifyFlags flags,
> +    AvbSlotVerifyData* data,
> +    AvbHashtreeErrorMode* out_hashtree_error_mode) {
> +  AvbHashtreeErrorMode ret = AVB_HASHTREE_ERROR_MODE_RESTART;
> +  AvbIOResult io_ret = AVB_IO_RESULT_OK;
> +  uint8_t vbmeta_digest_sha256[AVB_SHA256_DIGEST_SIZE];
> +  uint8_t stored_vbmeta_digest_sha256[AVB_SHA256_DIGEST_SIZE];
> +  size_t num_bytes_read;
> +
> +  avb_assert(out_hashtree_error_mode != NULL);
> +  avb_assert(ops->read_persistent_value != NULL);
> +  avb_assert(ops->write_persistent_value != NULL);
> +
> +  // If we're rebooting because of dm-verity corruption, make a note of
> +  // the vbmeta hash so we can stay in 'eio' mode until things change.
> +  if (flags & AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION) {
> +    avb_debug(
> +        "Rebooting because of dm-verity corruption - "
> +        "recording OS instance and using 'eio' mode.\n");
> +    avb_slot_verify_data_calculate_vbmeta_digest(
> +        data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest_sha256);
> +    io_ret = ops->write_persistent_value(ops,
> +                                         AVB_NPV_MANAGED_VERITY_MODE,
> +                                         AVB_SHA256_DIGEST_SIZE,
> +                                         vbmeta_digest_sha256);
> +    if (io_ret != AVB_IO_RESULT_OK) {
> +      avb_error("Error writing to " AVB_NPV_MANAGED_VERITY_MODE ".\n");
> +      goto out;
> +    }
> +    ret = AVB_HASHTREE_ERROR_MODE_EIO;
> +    io_ret = AVB_IO_RESULT_OK;
> +    goto out;
> +  }
> +
> +  // See if we're in 'eio' mode.
> +  io_ret = ops->read_persistent_value(ops,
> +                                      AVB_NPV_MANAGED_VERITY_MODE,
> +                                      AVB_SHA256_DIGEST_SIZE,
> +                                      stored_vbmeta_digest_sha256,
> +                                      &num_bytes_read);
> +  if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_VALUE ||
> +      (io_ret == AVB_IO_RESULT_OK && num_bytes_read == 0)) {
> +    // This is the usual case ('eio' mode not set).
> +    avb_debug("No dm-verity corruption - using in 'restart' mode.\n");
> +    ret = AVB_HASHTREE_ERROR_MODE_RESTART;
> +    io_ret = AVB_IO_RESULT_OK;
> +    goto out;
> +  } else if (io_ret != AVB_IO_RESULT_OK) {
> +    avb_error("Error reading from " AVB_NPV_MANAGED_VERITY_MODE ".\n");
> +    goto out;
> +  }
> +  if (num_bytes_read != AVB_SHA256_DIGEST_SIZE) {
> +    avb_error(
> +        "Unexpected number of bytes read from " AVB_NPV_MANAGED_VERITY_MODE
> +        ".\n");
> +    io_ret = AVB_IO_RESULT_ERROR_IO;
> +    goto out;
> +  }
> +
> +  // OK, so we're currently in 'eio' mode and the vbmeta digest of the OS
> +  // that caused this is in |stored_vbmeta_digest_sha256| ... now see if
> +  // the OS we're dealing with now is the same.
> +  avb_slot_verify_data_calculate_vbmeta_digest(
> +      data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest_sha256);
> +  if (avb_memcmp(vbmeta_digest_sha256,
> +                 stored_vbmeta_digest_sha256,
> +                 AVB_SHA256_DIGEST_SIZE) == 0) {
> +    // It's the same so we're still in 'eio' mode.
> +    avb_debug("Same OS instance detected - staying in 'eio' mode.\n");
> +    ret = AVB_HASHTREE_ERROR_MODE_EIO;
> +    io_ret = AVB_IO_RESULT_OK;
> +  } else {
> +    // It did change!
> +    avb_debug(
> +        "New OS instance detected - changing from 'eio' to 'restart' mode.\n");
> +    io_ret =
> +        ops->write_persistent_value(ops,
> +                                    AVB_NPV_MANAGED_VERITY_MODE,
> +                                    0,  // This clears the persistent property.
> +                                    vbmeta_digest_sha256);
> +    if (io_ret != AVB_IO_RESULT_OK) {
> +      avb_error("Error clearing " AVB_NPV_MANAGED_VERITY_MODE ".\n");
> +      goto out;
> +    }
> +    ret = AVB_HASHTREE_ERROR_MODE_RESTART;
> +    io_ret = AVB_IO_RESULT_OK;
> +  }
> +
> +out:
> +  *out_hashtree_error_mode = ret;
> +  return io_ret;
> +}
> +
> +static bool has_system_partition(AvbOps* ops, const char* ab_suffix) {
> +  char part_name[AVB_PART_NAME_MAX_SIZE];
> +  char* system_part_name = "system";
> +  char guid_buf[37];
> +  AvbIOResult io_ret;
> +
> +  if (!avb_str_concat(part_name,
> +                      sizeof part_name,
> +                      system_part_name,
> +                      avb_strlen(system_part_name),
> +                      ab_suffix,
> +                      avb_strlen(ab_suffix))) {
> +    avb_error("System partition name and suffix does not fit.\n");
> +    return false;
> +  }
> +
> +  io_ret = ops->get_unique_guid_for_partition(
> +      ops, part_name, guid_buf, sizeof guid_buf);
> +  if (io_ret == AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) {
> +    avb_debug("No system partition.\n");
> +    return false;
> +  } else if (io_ret != AVB_IO_RESULT_OK) {
> +    avb_error("Error getting unique GUID for system partition.\n");
> +    return false;
> +  }
> +
> +  return true;
> +}
> +
>  AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
>                                      const char* const* requested_partitions,
>                                      const char* ab_suffix,
> @@ -1087,14 +1355,10 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
>        (flags & AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR);
>    AvbCmdlineSubstList* additional_cmdline_subst = NULL;
>
> -  /* Fail early if we're missing the AvbOps needed for slot verification.
> -   *
> -   * For now, handle get_size_of_partition() not being implemented. In
> -   * a later release we may change that.
> -   */
> +  /* Fail early if we're missing the AvbOps needed for slot verification. */
>    avb_assert(ops->read_is_device_unlocked != NULL);
>    avb_assert(ops->read_from_partition != NULL);
> -  avb_assert(ops->validate_vbmeta_public_key != NULL);
> +  avb_assert(ops->get_size_of_partition != NULL);
>    avb_assert(ops->read_rollback_index != NULL);
>    avb_assert(ops->get_unique_guid_for_partition != NULL);
>
> @@ -1112,6 +1376,36 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
>      goto fail;
>    }
>
> +  /* Make sure passed-in AvbOps support persistent values if
> +   * asking for libavb to manage verity state.
> +   */
> +  if (hashtree_error_mode == AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
> +    if (ops->read_persistent_value == NULL ||
> +        ops->write_persistent_value == NULL) {
> +      avb_error(
> +          "Persistent values required for "
> +          "AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO "
> +          "but are not implemented in given AvbOps.\n");
> +      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
> +      goto fail;
> +    }
> +  }
> +
> +  /* Make sure passed-in AvbOps support verifying public keys and getting
> +   * rollback index location if not using a vbmeta partition.
> +   */
> +  if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) {
> +    if (ops->validate_public_key_for_partition == NULL) {
> +      avb_error(
> +          "AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION was passed but the "
> +          "validate_public_key_for_partition() operation isn't implemented.\n");
> +      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
> +      goto fail;
> +    }
> +  } else {
> +    avb_assert(ops->validate_vbmeta_public_key != NULL);
> +  }
> +
>    slot_data = avb_calloc(sizeof(AvbSlotVerifyData));
>    if (slot_data == NULL) {
>      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> @@ -1136,99 +1430,163 @@ AvbSlotVerifyResult avb_slot_verify(AvbOps* ops,
>      goto fail;
>    }
>
> -  ret = load_and_verify_vbmeta(ops,
> -                               requested_partitions,
> -                               ab_suffix,
> -                               allow_verification_error,
> -                               0 /* toplevel_vbmeta_flags */,
> -                               0 /* rollback_index_location */,
> -                               "vbmeta",
> -                               avb_strlen("vbmeta"),
> -                               NULL /* expected_public_key */,
> -                               0 /* expected_public_key_length */,
> -                               slot_data,
> -                               &algorithm_type,
> -                               additional_cmdline_subst);
> -  if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
> +  if (flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION) {
> +    if (requested_partitions == NULL || requested_partitions[0] == NULL) {
> +      avb_fatal(
> +          "Requested partitions cannot be empty when using "
> +          "AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION");
> +      ret = AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT;
> +      goto fail;
> +    }
> +
> +    /* No vbmeta partition, go through each of the requested partitions... */
> +    for (size_t n = 0; requested_partitions[n] != NULL; n++) {
> +      ret = load_and_verify_vbmeta(ops,
> +                                   requested_partitions,
> +                                   ab_suffix,
> +                                   flags,
> +                                   allow_verification_error,
> +                                   0 /* toplevel_vbmeta_flags */,
> +                                   0 /* rollback_index_location */,
> +                                   requested_partitions[n],
> +                                   avb_strlen(requested_partitions[n]),
> +                                   NULL /* expected_public_key */,
> +                                   0 /* expected_public_key_length */,
> +                                   slot_data,
> +                                   &algorithm_type,
> +                                   additional_cmdline_subst);
> +      if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
> +        goto fail;
> +      }
> +    }
> +
> +  } else {
> +    /* Usual path, load "vbmeta"... */
> +    ret = load_and_verify_vbmeta(ops,
> +                                 requested_partitions,
> +                                 ab_suffix,
> +                                 flags,
> +                                 allow_verification_error,
> +                                 0 /* toplevel_vbmeta_flags */,
> +                                 0 /* rollback_index_location */,
> +                                 "vbmeta",
> +                                 avb_strlen("vbmeta"),
> +                                 NULL /* expected_public_key */,
> +                                 0 /* expected_public_key_length */,
> +                                 slot_data,
> +                                 &algorithm_type,
> +                                 additional_cmdline_subst);
> +    if (!allow_verification_error && ret != AVB_SLOT_VERIFY_RESULT_OK) {
> +      goto fail;
> +    }
> +  }
> +
> +  if (!result_should_continue(ret)) {
>      goto fail;
>    }
>
>    /* If things check out, mangle the kernel command-line as needed. */
> -  if (result_should_continue(ret)) {
> +  if (!(flags & AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION)) {
>      if (avb_strcmp(slot_data->vbmeta_images[0].partition_name, "vbmeta") != 0) {
>        avb_assert(
>            avb_strcmp(slot_data->vbmeta_images[0].partition_name, "boot") == 0);
>        using_boot_for_vbmeta = true;
>      }
> +  }
>
> -    /* Byteswap top-level vbmeta header since we'll need it below. */
> -    avb_vbmeta_image_header_to_host_byte_order(
> -        (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data,
> -        &toplevel_vbmeta);
> +  /* Byteswap top-level vbmeta header since we'll need it below. */
> +  avb_vbmeta_image_header_to_host_byte_order(
> +      (const AvbVBMetaImageHeader*)slot_data->vbmeta_images[0].vbmeta_data,
> +      &toplevel_vbmeta);
>
> -    /* Fill in |ab_suffix| field. */
> -    slot_data->ab_suffix = avb_strdup(ab_suffix);
> -    if (slot_data->ab_suffix == NULL) {
> -      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> -      goto fail;
> -    }
> +  /* Fill in |ab_suffix| field. */
> +  slot_data->ab_suffix = avb_strdup(ab_suffix);
> +  if (slot_data->ab_suffix == NULL) {
> +    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> +    goto fail;
> +  }
>
> -    /* If verification is disabled, we are done ... we specifically
> -     * don't want to add any androidboot.* options since verification
> -     * is disabled.
> +  /* If verification is disabled, we are done ... we specifically
> +   * don't want to add any androidboot.* options since verification
> +   * is disabled.
> +   */
> +  if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
> +    /* Since verification is disabled we didn't process any
> +     * descriptors and thus there's no cmdline... so set root= such
> +     * that the system partition is mounted.
>       */
> -    if (toplevel_vbmeta.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
> -      /* Since verification is disabled we didn't process any
> -       * descriptors and thus there's no cmdline... so set root= such
> -       * that the system partition is mounted.
> -       */
> -      avb_assert(slot_data->cmdline == NULL);
> +    avb_assert(slot_data->cmdline == NULL);
> +    // Devices with dynamic partitions won't have system partition.
> +    // Instead, it has a large super partition to accommodate *.img files.
> +    // See b/119551429 for details.
> +    if (has_system_partition(ops, ab_suffix)) {
>        slot_data->cmdline =
>            avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");
> -      if (slot_data->cmdline == NULL) {
> -        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> -        goto fail;
> -      }
>      } else {
> -      /* Add options - any failure in avb_append_options() is either an
> -       * I/O or OOM error.
> -       */
> -      AvbSlotVerifyResult sub_ret = avb_append_options(ops,
> -                                                       slot_data,
> -                                                       &toplevel_vbmeta,
> -                                                       algorithm_type,
> -                                                       hashtree_error_mode);
> -      if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
> -        ret = sub_ret;
> -        goto fail;
> -      }
> +      // The |cmdline| field should be a NUL-terminated string.
> +      slot_data->cmdline = avb_strdup("");
>      }
> -
> -    /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
> -    if (slot_data->cmdline != NULL) {
> -      char* new_cmdline;
> -      new_cmdline = avb_sub_cmdline(ops,
> -                                    slot_data->cmdline,
> -                                    ab_suffix,
> -                                    using_boot_for_vbmeta,
> -                                    additional_cmdline_subst);
> -      if (new_cmdline != slot_data->cmdline) {
> -        if (new_cmdline == NULL) {
> +    if (slot_data->cmdline == NULL) {
> +      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> +      goto fail;
> +    }
> +  } else {
> +    /* If requested, manage dm-verity mode... */
> +    AvbHashtreeErrorMode resolved_hashtree_error_mode = hashtree_error_mode;
> +    if (hashtree_error_mode ==
> +        AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO) {
> +      AvbIOResult io_ret;
> +      io_ret = avb_manage_hashtree_error_mode(
> +          ops, flags, slot_data, &resolved_hashtree_error_mode);
> +      if (io_ret != AVB_IO_RESULT_OK) {
> +        ret = AVB_SLOT_VERIFY_RESULT_ERROR_IO;
> +        if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
>            ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> -          goto fail;
>          }
> -        avb_free(slot_data->cmdline);
> -        slot_data->cmdline = new_cmdline;
> +        goto fail;
>        }
>      }
> +    slot_data->resolved_hashtree_error_mode = resolved_hashtree_error_mode;
>
> -    if (out_data != NULL) {
> -      *out_data = slot_data;
> -    } else {
> -      avb_slot_verify_data_free(slot_data);
> +    /* Add options... */
> +    AvbSlotVerifyResult sub_ret;
> +    sub_ret = avb_append_options(ops,
> +                                 flags,
> +                                 slot_data,
> +                                 &toplevel_vbmeta,
> +                                 algorithm_type,
> +                                 hashtree_error_mode,
> +                                 resolved_hashtree_error_mode);
> +    if (sub_ret != AVB_SLOT_VERIFY_RESULT_OK) {
> +      ret = sub_ret;
> +      goto fail;
> +    }
> +  }
> +
> +  /* Substitute $(ANDROID_SYSTEM_PARTUUID) and friends. */
> +  if (slot_data->cmdline != NULL && avb_strlen(slot_data->cmdline) != 0) {
> +    char* new_cmdline;
> +    new_cmdline = avb_sub_cmdline(ops,
> +                                  slot_data->cmdline,
> +                                  ab_suffix,
> +                                  using_boot_for_vbmeta,
> +                                  additional_cmdline_subst);
> +    if (new_cmdline != slot_data->cmdline) {
> +      if (new_cmdline == NULL) {
> +        ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
> +        goto fail;
> +      }
> +      avb_free(slot_data->cmdline);
> +      slot_data->cmdline = new_cmdline;
>      }
>    }
>
> +  if (out_data != NULL) {
> +    *out_data = slot_data;
> +  } else {
> +    avb_slot_verify_data_free(slot_data);
> +  }
> +
>    avb_free_cmdline_subst_list(additional_cmdline_subst);
>    additional_cmdline_subst = NULL;
>
> diff --git a/lib/libavb/avb_slot_verify.h b/lib/libavb/avb_slot_verify.h
> index 73fd70d4ce..8d0fa53693 100644
> --- a/lib/libavb/avb_slot_verify.h
> +++ b/lib/libavb/avb_slot_verify.h
> @@ -51,12 +51,25 @@ typedef enum {
>   * be used ONLY for diagnostics and debugging. It cannot be used
>   * unless AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is also
>   * used.
> + *
> + * AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO means that either
> + * AVB_HASHTREE_ERROR_MODE_RESTART or AVB_HASHTREE_ERROR_MODE_EIO is used
> + * depending on state. This mode implements a state machine whereby
> + * AVB_HASHTREE_ERROR_MODE_RESTART is used by default and when
> + * AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION is passed the
> + * mode transitions to AVB_HASHTREE_ERROR_MODE_EIO. When a new OS has been
> + * detected the device transitions back to the AVB_HASHTREE_ERROR_MODE_RESTART
> + * mode. To do this persistent storage is needed - specifically this means that
> + * the passed in AvbOps will need to have the read_persistent_value() and
> + * write_persistent_value() operations implemented. The name of the persistent
> + * value used is "avb.managed_verity_mode" and 32 bytes of storage is needed.
>   */
>  typedef enum {
>    AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
>    AVB_HASHTREE_ERROR_MODE_RESTART,
>    AVB_HASHTREE_ERROR_MODE_EIO,
> -  AVB_HASHTREE_ERROR_MODE_LOGGING
> +  AVB_HASHTREE_ERROR_MODE_LOGGING,
> +  AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO
>  } AvbHashtreeErrorMode;
>
>  /* Flags that influence how avb_slot_verify() works.
> @@ -80,10 +93,26 @@ typedef enum {
>   * contents loaded from |requested_partition| will be the contents of
>   * the entire partition instead of just the size specified in the hash
>   * descriptor.
> + *
> + * The AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION flag
> + * should be set if using AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO
> + * and the reason the boot loader is running is because the device
> + * was restarted by the dm-verity driver.
> + *
> + * If the AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION flag is set then
> + * data won't be loaded from the "vbmeta" partition and the
> + * |validate_vbmeta_public_key| operation is never called. Instead, the
> + * vbmeta structs in |requested_partitions| are loaded and processed and the
> + * |validate_public_key_for_partition| operation is called for each of these
> + * vbmeta structs. This flag is useful when booting into recovery on a device
> + * not using A/B - see section "Booting into recovery" in README.md for
> + * more information.
>   */
>  typedef enum {
>    AVB_SLOT_VERIFY_FLAGS_NONE = 0,
> -  AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR = (1 << 0)
> +  AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR = (1 << 0),
> +  AVB_SLOT_VERIFY_FLAGS_RESTART_CAUSED_BY_HASHTREE_CORRUPTION = (1 << 1),
> +  AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION = (1 << 2),
>  } AvbSlotVerifyFlags;
>
>  /* Get a textual representation of |result|. */
> @@ -188,6 +217,10 @@ typedef struct {
>   *   set to AVB_HASHTREE_ERROR_MODE_EIO, and 'logging' if it's set to
>   *   AVB_HASHTREE_ERROR_MODE_LOGGING.
>   *
> + *   androidboot.veritymode.managed: This is set to 'yes' only
> + *   if hashtree validation isn't disabled and the passed-in hashtree
> + *   error mode is AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO.
> + *
>   *   androidboot.vbmeta.invalidate_on_error: This is set to 'yes' only
>   *   if hashtree validation isn't disabled and the passed-in hashtree
>   *   error mode is AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE.
> @@ -203,7 +236,9 @@ typedef struct {
>   *   PARTUUID=$(ANDROID_VBMETA_PARTUUID) before substitution so it
>   *   will end up pointing to the vbmeta partition for the verified
>   *   slot. If there is no vbmeta partition it will point to the boot
> - *   partition of the verified slot.
> + *   partition of the verified slot. If the flag
> + *   AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION is used, this is not
> + *   set.
>   *
>   *   androidboot.vbmeta.avb_version: This is set to the decimal value
>   *   of AVB_VERSION_MAJOR followed by a dot followed by the decimal
> @@ -228,6 +263,15 @@ typedef struct {
>   * appropriate system partition is substituted in. Note that none of
>   * the androidboot.* options mentioned above will be set.
>   *
> + * The |resolved_hashtree_error_mode| is the the value of the passed
> + * avb_slot_verify()'s |hashtree_error_mode| parameter except that it never has
> + * the value AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO. If this value was
> + * passed in, then the restart/eio state machine is used resulting in
> + * |resolved_hashtree_error_mode| being set to either
> + * AVB_HASHTREE_ERROR_MODE_RESTART or AVB_HASHTREE_ERROR_MODE_EIO.  If set to
> + * AVB_HASHTREE_ERROR_MODE_EIO the boot loader should present a RED warning
> + * screen for the user to click through before continuing to boot.
> + *
>   * This struct may grow in the future without it being considered an
>   * ABI break.
>   */
> @@ -239,6 +283,7 @@ typedef struct {
>    size_t num_loaded_partitions;
>    char* cmdline;
>    uint64_t rollback_indexes[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS];
> +  AvbHashtreeErrorMode resolved_hashtree_error_mode;
>  } AvbSlotVerifyData;
>
>  /* Calculates a digest of all vbmeta images in |data| using
> @@ -282,12 +327,8 @@ void avb_slot_verify_data_free(AvbSlotVerifyData* data);
>   * ignore verification errors which is something needed in the
>   * UNLOCKED state. See the AvbSlotVerifyFlags enumeration for details.
>   *
> - * The |hashtree_error_mode| parameter should be set to the desired
> - * error handling mode when hashtree validation fails inside the
> - * HLOS. This value isn't used by libavb per se - it is forwarded to
> - * the HLOS through the androidboot.veritymode and
> - * androidboot.vbmeta.invalidate_on_error cmdline parameters. See the
> - * AvbHashtreeErrorMode enumeration for details.
> + * The |hashtree_error_mode| parameter should be set to the desired error
> + * handling mode. See the AvbHashtreeErrorMode enumeration for details.
>   *
>   * Also note that |out_data| is never set if
>   * AVB_SLOT_VERIFY_RESULT_ERROR_OOM, AVB_SLOT_VERIFY_RESULT_ERROR_IO,
> diff --git a/lib/libavb/avb_sysdeps.h b/lib/libavb/avb_sysdeps.h
> index f032de4a2e..f52428cc62 100644
> --- a/lib/libavb/avb_sysdeps.h
> +++ b/lib/libavb/avb_sysdeps.h
> @@ -53,6 +53,14 @@ int avb_memcmp(const void* src1,
>   */
>  int avb_strcmp(const char* s1, const char* s2);
>
> +/* Compare |n| bytes in two strings.
> + *
> + * Return an integer less than, equal to, or greater than zero if the
> + * first |n| bytes of |s1| is found, respectively, to be less than,
> + * to match, or be greater than the first |n| bytes of |s2|.
> + */
> +int avb_strncmp(const char* s1, const char* s2, size_t n);
> +
>  /* Copy |n| bytes from |src| to |dest|. */
>  void* avb_memcpy(void* dest, const void* src, size_t n);
>
> diff --git a/lib/libavb/avb_sysdeps_posix.c b/lib/libavb/avb_sysdeps_posix.c
> index e9addc1c87..4ccf41e428 100644
> --- a/lib/libavb/avb_sysdeps_posix.c
> +++ b/lib/libavb/avb_sysdeps_posix.c
> @@ -24,14 +24,12 @@ int avb_strcmp(const char* s1, const char* s2) {
>    return strcmp(s1, s2);
>  }
>
> -size_t avb_strlen(const char* str) {
> -  return strlen(str);
> +int avb_strncmp(const char* s1, const char* s2, size_t n) {
> +  return strncmp(s1, s2, n);
>  }
>
> -uint32_t avb_div_by_10(uint64_t* dividend) {
> -  uint32_t rem = (uint32_t)(*dividend % 10);
> -  *dividend /= 10;
> -  return rem;
> +size_t avb_strlen(const char* str) {
> +  return strlen(str);
>  }
>
>  void avb_abort(void) {
> @@ -60,3 +58,9 @@ void* avb_malloc_(size_t size) {
>  void avb_free(void* ptr) {
>    free(ptr);
>  }
> +
> +uint32_t avb_div_by_10(uint64_t* dividend) {
> +  uint32_t rem = (uint32_t)(*dividend % 10);
> +  *dividend /= 10;
> +  return rem;
> +}
> diff --git a/lib/libavb/avb_vbmeta_image.c b/lib/libavb/avb_vbmeta_image.c
> index a7e2322b9e..384f5ac19e 100644
> --- a/lib/libavb/avb_vbmeta_image.c
> +++ b/lib/libavb/avb_vbmeta_image.c
> @@ -35,17 +35,18 @@ AvbVBMetaVerifyResult avb_vbmeta_image_verify(
>      *out_public_key_length = 0;
>    }
>
> +  /* Before we byteswap or compare Magic, ensure length is long enough. */
> +  if (length < sizeof(AvbVBMetaImageHeader)) {
> +    avb_error("Length is smaller than header.\n");
> +    goto out;
> +  }
> +
>    /* Ensure magic is correct. */
>    if (avb_safe_memcmp(data, AVB_MAGIC, AVB_MAGIC_LEN) != 0) {
>      avb_error("Magic is incorrect.\n");
>      goto out;
>    }
>
> -  /* Before we byteswap, ensure length is long enough. */
> -  if (length < sizeof(AvbVBMetaImageHeader)) {
> -    avb_error("Length is smaller than header.\n");
> -    goto out;
> -  }
>    avb_vbmeta_image_header_to_host_byte_order((const AvbVBMetaImageHeader*)data,
>                                               &h);
>
> --
> 2.23.0.rc1
>

Acked-by: Igor Opaniuk <igor.opaniuk at gmail.com>

-- 
Best regards - Freundliche GrĂ¼sse - Meilleures salutations

Igor Opaniuk

mailto: igor.opaniuk at gmail.com
skype: igor.opanyuk
+380 (93) 836 40 67
http://ua.linkedin.com/in/iopaniuk


More information about the U-Boot mailing list