Adjust the used size of the bloblist up to the record alignment before
adding a record.

A transfer list from another boot stage can have a used size that ends
with the data of its last entry, which does not have to be a multiple of
8 bytes. TF-A's transfer list library creates lists like this. The next
entry still starts 8-byte aligned.

With such a list, bloblist_addrec() works out an alignment gap that is
smaller than a record header and tries to fill it with a void record of
negative size. That call sees the same used size, so it recurses until
the stack overflows.

Fixes: f9ef9fb033d5 ("bloblist: Handle alignment with a void entry")
Signed-off-by: David Lechner <[email protected]>
---
 common/bloblist.c      |  6 ++++++
 test/common/bloblist.c | 25 +++++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/common/bloblist.c b/common/bloblist.c
index 846c8047f74..4ddb4a56944 100644
--- a/common/bloblist.c
+++ b/common/bloblist.c
@@ -169,6 +169,12 @@ static int bloblist_addrec(uint tag, int size, int 
align_log2,
        if (!align_log2)
                align_log2 = BLOBLIST_BLOB_ALIGN_LOG2;
 
+       /*
+        * The data of the last record does not have to end aligned, but the
+        * next record starts aligned.
+        */
+       hdr->used_size = ALIGN(hdr->used_size, BLOBLIST_BLOB_ALIGN);
+
        /* Figure out where the new data will start */
        data_start = map_to_sysmem(hdr) + hdr->used_size + sizeof(*rec);
 
diff --git a/test/common/bloblist.c b/test/common/bloblist.c
index 797bde27025..ef410ca62fc 100644
--- a/test/common/bloblist.c
+++ b/test/common/bloblist.c
@@ -360,6 +360,31 @@ static int bloblist_test_align(struct unit_test_state *uts)
 }
 BLOBLIST_TEST(bloblist_test_align, UFT_BLOBLIST);
 
+/* Test adding a record after one whose data does not end aligned */
+static int bloblist_test_align_used_size(struct unit_test_state *uts)
+{
+       struct bloblist_hdr *hdr;
+       char *data;
+
+       hdr = clear_bloblist();
+       ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+       data = bloblist_add(TEST_TAG, TEST_SIZE, 0);
+       ut_assertnonnull(data);
+
+       /* Another boot phase may end the used size with the data */
+       hdr->used_size = map_to_sysmem(data) + TEST_SIZE - TEST_ADDR;
+       ut_assert(hdr->used_size & (BLOBLIST_BLOB_ALIGN - 1));
+
+       data = bloblist_add(TEST_TAG2, TEST_SIZE2, 0);
+       ut_assertnonnull(data);
+       ut_asserteq(0, map_to_sysmem(data) & (BLOBLIST_BLOB_ALIGN - 1));
+       ut_asserteq_ptr(data, bloblist_find(TEST_TAG2, TEST_SIZE2));
+       ut_assertnonnull(bloblist_find(TEST_TAG, TEST_SIZE));
+
+       return 0;
+}
+BLOBLIST_TEST(bloblist_test_align_used_size, UFT_BLOBLIST);
+
 /* Test relocation of a bloblist */
 static int bloblist_test_reloc(struct unit_test_state *uts)
 {

-- 
2.53.0

Reply via email to