The simple malloc used in SPL has a no-op free(), so allocating the
GPT partition entries buffer on every partition table read leaks
memory. With repeated lookups, SPL can exhaust its small heap.

Keep a single cached buffer and reuse it while the allocation size
does not change. The entries are re-read on every call, so the buffer
is device- and LBA-agnostic.

It is safe to reuse the same buffer in the single-threaded xPL context
as the returned pointer is only ever handed out within a single
partition-table lookup and every caller consumes or copies the entries
before the next lookup.

The change is gated on CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE), so builds
with a real free() (U-Boot proper) keep the original malloc/free behavior.

Signed-off-by: Benoît Monin <[email protected]>
---
 disk/part_efi.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/disk/part_efi.c b/disk/part_efi.c
index d8b17ec2e91..8a57fe911c4 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -1141,7 +1141,7 @@ static int find_valid_gpt(struct blk_desc *desc, 
gpt_header *gpt_head,
 static gpt_entry *alloc_read_gpt_entries(struct blk_desc *desc,
                                         gpt_header *pgpt_head)
 {
-       size_t count = 0, blk_cnt;
+       size_t count = 0, blk_cnt, size;
        lbaint_t blk;
        gpt_entry *pte = NULL;
 
@@ -1158,10 +1158,27 @@ static gpt_entry *alloc_read_gpt_entries(struct 
blk_desc *desc,
                  (u32)le32_to_cpu(pgpt_head->sizeof_partition_entry),
                  (ulong)count);
 
+       size = PAD_TO_BLOCKSIZE(count, desc);
+
        /* Allocate memory for PTE, remember to FREE */
-       if (count != 0) {
-               pte = memalign(ARCH_DMA_MINALIGN,
-                              PAD_TO_BLOCKSIZE(count, desc));
+       if (CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE)) {
+               /*
+                * The simple malloc has a no-op free(): keep one
+                * buffer and reuse it. The entries are re-read on
+                * every call, so the buffer is device- and LBA-agnostic.
+                */
+               static gpt_entry *cache;
+               static size_t cache_size;
+
+               if (cache && cache_size == size) {
+                       pte = cache;
+               } else {
+                       pte = memalign(ARCH_DMA_MINALIGN, size);
+                       cache = pte;
+                       cache_size = size;
+               }
+       } else {
+               pte = memalign(ARCH_DMA_MINALIGN, size);
        }
 
        if (count == 0 || pte == NULL) {

-- 
2.55.0

Reply via email to