[Concept,15/37] malloc: work around some memalign fragmentation issues

Message ID 20251201170529.3237986-16-sjg@u-boot.org
State New
Headers
Series malloc: Import dlmalloc 2.8.6 |

Commit Message

Simon Glass Dec. 1, 2025, 5:04 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Use of memalign can trigger fragmentation issues where the over-sized
allocation needed to guarantee alignment fails, even though the exact
user-requested size would succeed and be properly aligned.

If the padded allocation fails, try allocating exactly the user's
requested size. If that happens to be aligned, return it. Otherwise,
try a third allocation with just enough extra space to achieve
alignment.

Changes from original commits:
- Port to dlmalloc 2.8.6 internal_memalign() instead of mEMALIGn()
- Use internal_malloc/internal_free instead of mALLOc/fREe

Signed-off-by: Stephen Warren <swarren@nvidia.com>
Reviewed-by: Tom Rini <trini@konsulko.com>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
(cherry picked from 4f144a416469c6a29127b0656523ae628ea7cbaf)
---

 common/dlmalloc.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
  

Patch

diff --git a/common/dlmalloc.c b/common/dlmalloc.c
index 268d3fea52a..5a8e463671c 100644
--- a/common/dlmalloc.c
+++ b/common/dlmalloc.c
@@ -4975,6 +4975,46 @@  static void* internal_memalign(mstate m, size_t alignment, size_t bytes) {
     size_t nb = request2size(bytes);
     size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD;
     mem = internal_malloc(m, req);
+#ifdef __UBOOT__
+    /*
+     * The attempt to over-allocate (with a size large enough to guarantee the
+     * ability to find an aligned region within allocated memory) failed.
+     *
+     * Try again, this time only allocating exactly the size the user wants.
+     * If the allocation now succeeds and just happens to be aligned, we can
+     * still fulfill the user's request.
+     */
+    if (mem == 0) {
+      size_t extra, extra2;
+
+      mem = internal_malloc(m, bytes);
+      /* Aligned -> use it */
+      if (mem != 0 && (((size_t)(mem)) & (alignment - 1)) == 0)
+        return mem;
+      /*
+       * Otherwise, try again, requesting enough extra space to be able to
+       * acquire alignment.
+       */
+      if (mem != 0) {
+        internal_free(m, mem);
+        /* Add in extra bytes to match misalignment of unexpanded alloc */
+        extra = alignment - (((size_t)(mem)) % alignment);
+        mem = internal_malloc(m, bytes + extra);
+        /*
+         * mem might not be the same as before. Validate that the previous
+         * value of extra still works for the current value of mem.
+         */
+        if (mem != 0) {
+          extra2 = alignment - (((size_t)(mem)) % alignment);
+          if (extra2 > extra) {
+            internal_free(m, mem);
+            mem = 0;
+          }
+        }
+      }
+      /* Fall through to original NULL check and chunk splitting logic */
+    }
+#endif
     if (mem != 0) {
       mchunkptr p = mem2chunk(mem);
       if (PREACTION(m))