[avb] Check partition before allocating 64KB memory for VBMeta

This cl is an optimization for load_and_verify_vbmeta(). When
we want to load the VBMeta from the partition `vbmeta` and we
are the main VBMeta, prior to this cl, we first allocated 64KB
memory and then tried to read the partition `vbmeta`.

This cl added a check before this action. After this cl, we
first try to read 1 byte from the partition, if this fails
because no such partition exists, we fallback directly on
the `boot` partition. With this approach, we avoid allocating
the big chunk of memory on heap for VBMeta when the partition
doesn't exist.

This unblocks the cl aosp/2388176 where we don't have the
`vbmeta` partition and also cannot afford allocating the 64KB
memory in pVM firmware.

Bug: 256148034
Test: atest libavb_host_unittest libpvmfw_avb.integration_test
Change-Id: Ia242ddedc8bb5e8a4277a9a766d563d8ae8cdd60
diff --git a/lib/libavb/avb_slot_verify.c b/lib/libavb/avb_slot_verify.c
index 7467daa..3e76deb 100644
--- a/lib/libavb/avb_slot_verify.c
+++ b/lib/libavb/avb_slot_verify.c
@@ -26,6 +26,9 @@
 /* Maximum size of a vbmeta image - 64 KiB. */
 #define VBMETA_MAX_SIZE (64 * 1024)
 
+/* Test buffer used to check the existence of a partition. */
+#define TEST_BUFFER_SIZE 1
+
 static AvbSlotVerifyResult initialize_persistent_digest(
     AvbOps* ops,
     const char* part_name,
@@ -538,6 +541,22 @@
   return ret;
 }
 
+static AvbIOResult read_one_byte_from_partition(AvbOps* ops,
+                                                const char* partition_name) {
+  /* test_buf is a one-byte buffer used to check the existence of the
+   * current partition before allocating the big chunk of memory on heap
+   * for vbmeta later.
+   */
+  uint8_t test_buf[TEST_BUFFER_SIZE];
+  size_t test_num_read;
+  return ops->read_from_partition(ops,
+                                  partition_name,
+                                  0 /* offset */,
+                                  TEST_BUFFER_SIZE,
+                                  test_buf,
+                                  &test_num_read);
+}
+
 static AvbSlotVerifyResult load_and_verify_vbmeta(
     AvbOps* ops,
     const char* const* requested_partitions,
@@ -657,30 +676,38 @@
     }
   }
 
-  vbmeta_buf = avb_malloc(vbmeta_size);
-  if (vbmeta_buf == NULL) {
-    ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
-    goto out;
-  }
+  /* Read one byte from the partition to check the existence of the
+   * partition before allocating the big chunk of memory on heap
+   * for vbmeta later. `io_ret` will be used later to decide whether
+   * to fallback on the `boot` partition.
+   */
+  io_ret = read_one_byte_from_partition(ops, full_partition_name);
+  if (io_ret != AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION) {
+    vbmeta_buf = avb_malloc(vbmeta_size);
+    if (vbmeta_buf == NULL) {
+      ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
+      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);
-  }
+    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,
-                                    vbmeta_size,
-                                    vbmeta_buf,
-                                    &vbmeta_num_read);
+    io_ret = ops->read_from_partition(ops,
+                                      full_partition_name,
+                                      vbmeta_offset,
+                                      vbmeta_size,
+                                      vbmeta_buf,
+                                      &vbmeta_num_read);
+  }
   if (io_ret == AVB_IO_RESULT_ERROR_OOM) {
     ret = AVB_SLOT_VERIFY_RESULT_ERROR_OOM;
     goto out;