libavb: Update libavb to current AOSP master
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>
diff --git a/lib/libavb/avb_cmdline.c b/lib/libavb/avb_cmdline.c
index d246699..cb5b98e 100644
--- a/lib/libavb/avb_cmdline.c
+++ b/lib/libavb/avb_cmdline.c
@@ -39,6 +39,14 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 9af3a99..96539d8 100644
--- a/lib/libavb/avb_cmdline.h
+++ b/lib/libavb/avb_cmdline.h
@@ -43,10 +43,12 @@
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 fb0b305..9f03b97 100644
--- a/lib/libavb/avb_descriptor.c
+++ b/lib/libavb/avb_descriptor.c
@@ -72,7 +72,11 @@
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 @@
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 8bbdc7c..6a5c589 100644
--- a/lib/libavb/avb_ops.h
+++ b/lib/libavb/avb_ops.h
@@ -18,6 +18,7 @@
/* 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 @@
*
* 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 @@
* (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 @@
* 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 @@
* 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 365aaad..f5d02e0 100644
--- a/lib/libavb/avb_sha.h
+++ b/lib/libavb/avb_sha.h
@@ -31,8 +31,8 @@
/* 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 @@
/* 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 @@
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 @@
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 d24c701..86ecca5 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 @@
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 @@
}
}
-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 @@
}
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 @@
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 a5e7297..b19054f 100644
--- a/lib/libavb/avb_sha512.c
+++ b/lib/libavb/avb_sha512.c
@@ -127,14 +127,14 @@
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 @@
}
}
-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 @@
}
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 @@
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 a941850..5d400b3 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 @@
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 @@
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 @@
*/
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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;
+ 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;
+ }
}
-
- /* 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;
- }
-
- vbmeta_offset = footer.vbmeta_offset;
- vbmeta_size = footer.vbmeta_size;
}
vbmeta_buf = avb_malloc(vbmeta_size);
@@ -557,6 +660,18 @@
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 @@
* 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 @@
}
}
+ 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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
(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 @@
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,97 +1430,161 @@
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) {
+ /* 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 (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);
+ // 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)");
+ } else {
+ // The |cmdline| field should be a NUL-terminated string.
+ slot_data->cmdline = avb_strdup("");
+ }
+ 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;
+ }
+ }
+ slot_data->resolved_hashtree_error_mode = resolved_hashtree_error_mode;
- /* 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.
- */
- avb_assert(slot_data->cmdline == NULL);
- slot_data->cmdline =
- avb_strdup("root=PARTUUID=$(ANDROID_SYSTEM_PARTUUID)");
- if (slot_data->cmdline == NULL) {
+ /* 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;
}
- } 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;
- }
+ avb_free(slot_data->cmdline);
+ slot_data->cmdline = new_cmdline;
}
+ }
- /* 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) {
- 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);
- }
+ if (out_data != NULL) {
+ *out_data = slot_data;
+ } else {
+ avb_slot_verify_data_free(slot_data);
}
avb_free_cmdline_subst_list(additional_cmdline_subst);
diff --git a/lib/libavb/avb_slot_verify.h b/lib/libavb/avb_slot_verify.h
index 73fd70d..8d0fa53 100644
--- a/lib/libavb/avb_slot_verify.h
+++ b/lib/libavb/avb_slot_verify.h
@@ -51,12 +51,25 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
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 @@
* 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 f032de4..f52428c 100644
--- a/lib/libavb/avb_sysdeps.h
+++ b/lib/libavb/avb_sysdeps.h
@@ -53,6 +53,14 @@
*/
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 e9addc1..4ccf41e 100644
--- a/lib/libavb/avb_sysdeps_posix.c
+++ b/lib/libavb/avb_sysdeps_posix.c
@@ -24,14 +24,12 @@
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_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 a7e2322..384f5ac 100644
--- a/lib/libavb/avb_vbmeta_image.c
+++ b/lib/libavb/avb_vbmeta_image.c
@@ -35,17 +35,18 @@
*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);