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