Message ID | 20190815200403.1152-1-semen.protsenko@linaro.org |
---|---|
State | Accepted |
Commit | 4d579a4394d665b95c5289b5b7c9ce344e07bf18 |
Delegated to: | Tom Rini |
Headers | show |
Series | [U-Boot,1/2] libavb: Update libavb to current AOSP master | expand |
Hi Sam, On Thu, Aug 15, 2019 at 11:04:02PM +0300, Sam Protsenko 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@linaro.org> Thanks for the efforts. I get the same result, except the following minor difference [*]. The diff is minor and non-functional, but I think it's worth staying closer to AOSP. I will post my Reviewed-by signature, once this is addressed. TIA. [*] diff --git a/lib/libavb/avb_sysdeps_posix.c b/lib/libavb/avb_sysdeps_posix.c index 4ccf41e42834..74a37a949496 100644 --- a/lib/libavb/avb_sysdeps_posix.c +++ b/lib/libavb/avb_sysdeps_posix.c @@ -32,6 +32,12 @@ size_t avb_strlen(const char* str) { return strlen(str); } +uint32_t avb_div_by_10(uint64_t* dividend) { + uint32_t rem = (uint32_t)(*dividend % 10); + *dividend /= 10; + return rem; +} + void avb_abort(void) { hang(); } @@ -58,9 +64,3 @@ 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; -}
Hi Eugeniu, On Fri, Aug 16, 2019 at 1:36 PM Eugeniu Rosca <erosca@de.adit-jv.com> wrote: > > Hi Sam, > > On Thu, Aug 15, 2019 at 11:04:02PM +0300, Sam Protsenko 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@linaro.org> > > Thanks for the efforts. I get the same result, except the following > minor difference [*]. The diff is minor and non-functional, but I > think it's worth staying closer to AOSP. > I've used most recent commit on master branch of external/avb project of AOSP, and I can see that avb_div_by_10() is sitting in the end of file: [1]. Not sure why you have different result... Are you looking to different branch/commit perhaps? [1] https://android.googlesource.com/platform/external/avb/+/5fbb42a189aabb9a0bb5c3a8df89c9baf828a0d1/libavb/avb_sysdeps_posix.c#84 > I will post my Reviewed-by signature, once this is addressed. TIA. > > [*] diff --git a/lib/libavb/avb_sysdeps_posix.c b/lib/libavb/avb_sysdeps_posix.c > index 4ccf41e42834..74a37a949496 100644 > --- a/lib/libavb/avb_sysdeps_posix.c > +++ b/lib/libavb/avb_sysdeps_posix.c > @@ -32,6 +32,12 @@ size_t avb_strlen(const char* str) { > return strlen(str); > } > > +uint32_t avb_div_by_10(uint64_t* dividend) { > + uint32_t rem = (uint32_t)(*dividend % 10); > + *dividend /= 10; > + return rem; > +} > + > void avb_abort(void) { > hang(); > } > @@ -58,9 +64,3 @@ 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; > -} > > -- > Best Regards, > Eugeniu.
On Fri, Aug 16, 2019 at 01:59:20PM +0300, Sam Protsenko wrote: [..] > On Fri, Aug 16, 2019 at 1:36 PM Eugeniu Rosca <erosca@de.adit-jv.com> wrote: > > Thanks for the efforts. I get the same result, except the following > > minor difference [*]. The diff is minor and non-functional, but I > > think it's worth staying closer to AOSP. > > I've used most recent commit on master branch of external/avb project > of AOSP, and I can see that avb_div_by_10() is sitting in the end of > file: [1]. Not sure why you have different result... Are you looking > to different branch/commit perhaps? > > [1] https://android.googlesource.com/platform/external/avb/+/5fbb42a189aabb9a0bb5c3a8df89c9baf828a0d1/libavb/avb_sysdeps_posix.c#84 Small amendment. Since I effectively cherry picked the AOSP commits from AVB into U-Boot (as described in [2]), my result left the avb_div_by_10() function *in place* (as none of the imported AOSP commits does any change to this function). That led to the mismatch between our results. IOW what this patch appears to do *in addition* to importing the list of AOSP commits mentioned in [2] is repositioning the avb_div_by_10() such that it matches the AOSP. I obviously have no concerns about that. [2] https://patchwork.ozlabs.org/patch/1144801/#2235834 Having said that: Reviewed-by: Eugeniu Rosca <rosca.eugeniu@gmail.com> Thanks!
Hi Sam, On Thu, Aug 15, 2019 at 11:04 PM Sam Protsenko <semen.protsenko@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@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@gmail.com>
On Thu, Aug 15, 2019 at 11:04:02PM +0300, Sam Protsenko 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@linaro.org> > Reviewed-by: Eugeniu Rosca <rosca.eugeniu@gmail.com> > Acked-by: Igor Opaniuk <igor.opaniuk@gmail.com> Applied to u-boot/master, thanks!
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);
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@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(-)