[PATCH v2] malloc: Support hugepages in mremap_chunk

William Hunt william.hunt@arm.com
Wed Aug 20 18:01:43 GMT 2025


Add mremap_chunk support for mmap()ed chunks using hugepages by accounting for 
their alignment, to prevent the mremap call failing in most cases where the 
size passed is not a hugepage size multiple. It also improves robustness for 
reallocating hugepages since mremap is much less likely to fail, so running 
out of memory when reallocating a larger size and having to copy the old 
contents after mremap fails is also less likely.

To track whether an mmap()ed chunk uses hugepages, have a flag in the lowest 
bit of the mchunk_prev_size field which is set after a call to sysmalloc_mmap, 
and accessed later in mremap_chunk. Create macros for getting and setting this 
bit, and for mapping the bit off when accessing the field for mmap()ed chunks. 
Since the alignment cannot be lower than 8 bits, this flag cannot affect the 
alignment data. 

Update mem2chunk in malloc-check.c to check whether the memory is from a top 
chunk, to prevent later checks from triggering a SEGSEGV by attempting to 
access the mchunk_prev_size field of the next chunk. This bug caused the 
malloc/tst-tcfree4-malloc-check test to fail when ran using hugepages with
glibc.malloc.hugetlb=2 set, since the main arena is marked non-contiguous and 
the previous checks that would cause NULL to be returned do not run before the 
next chunk is attempted to be accessed. 

Add malloc/tst-tcfree4-malloc-check to the tests-exclude-malloc-check list as 
malloc-check prevents the tcache from being used to store chunks.

Changes from v1:
- Change macros to inlined functions.
- Added malloc/tst-tcfree4 to tests-exclude-malloc-check as tcache is disabled.

Passed regress for THPs, neither and HPs using overcommit pages, OK for commit?
---
 malloc/Makefile       |  1 +
 malloc/malloc-check.c |  6 +++---
 malloc/malloc.c       | 41 +++++++++++++++++++++++++++++++++++------
 3 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/malloc/Makefile b/malloc/Makefile
index 83f6c873e8..a9a0e87264 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -114,6 +114,7 @@ tests-exclude-malloc-check = \
   tst-memalign-3 \
   tst-mxfast \
   tst-safe-linking \
+  tst-tcfree4 \
 # tests-exclude-malloc-check
 
 # Run all tests with MALLOC_CHECK_=3
diff --git a/malloc/malloc-check.c b/malloc/malloc-check.c
index 9532316a29..350086e58c 100644
--- a/malloc/malloc-check.c
+++ b/malloc/malloc-check.c
@@ -121,7 +121,7 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
     {
       /* Must be a chunk in conventional heap memory. */
       int contig = contiguous (&main_arena);
-      if ((contig &&
+      if (((contig || p == main_arena.top) &&
            ((char *) p < mp_.sbrk_base ||
             ((char *) p + sz) >= (mp_.sbrk_base + main_arena.system_mem))) ||
           sz < MINSIZE || sz & MALLOC_ALIGN_MASK || !inuse (p) ||
@@ -151,8 +151,8 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
            offset != 0x200 && offset != 0x400 && offset != 0x800 && offset != 0x1000 &&
            offset < 0x2000) ||
           !chunk_is_mmapped (p) || prev_inuse (p) ||
-          ((((unsigned long) p - prev_size (p)) & page_mask) != 0) ||
-          ((prev_size (p) + sz) & page_mask) != 0)
+          ((((unsigned long) p - prev_size_mmap (p)) & page_mask) != 0) ||
+          ((prev_size_mmap (p) + sz) & page_mask) != 0)
         return NULL;
 
       for (sz = CHUNK_HDR_SZ + memsize (p) - 1;
diff --git a/malloc/malloc.c b/malloc/malloc.c
index e08873cad5..55688d4319 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1477,6 +1477,30 @@ tag_new_usable (void *ptr)
   return ptr;
 }
 
+/* HP page used for an mmap()'ed chunk. */
+#define MMAP_HP 0x1
+
+/* Check for HP usage from an mmap()'ed chunk. */
+static __always_inline bool
+mmap_is_hp (mchunkptr p)
+{
+  return prev_size (p) & MMAP_HP;
+}
+
+/* Set HP advised field for an mmap()'ed chunk. */
+static __always_inline void
+set_mmap_is_hp (mchunkptr p)
+{
+  prev_size (p) |= MMAP_HP;
+}
+
+/* Get an mmap()ed chunk's offset, ignoring huge page bits. */
+static __always_inline size_t
+prev_size_mmap (mchunkptr p)
+{
+  return prev_size (p) & ~MMAP_HP;
+}
+
 /*
    -------------------- Internal data structures --------------------
 
@@ -2105,7 +2129,7 @@ do_check_chunk (mstate av, mchunkptr p)
   else
     {
       /* chunk is page-aligned */
-      assert (((prev_size (p) + sz) & (GLRO (dl_pagesize) - 1)) == 0);
+      assert (((prev_size_mmap (p) + sz) & (GLRO (dl_pagesize) - 1)) == 0);
       /* mem is aligned */
       assert (!misaligned_chunk (p));
     }
@@ -2429,6 +2453,11 @@ sysmalloc_mmap (INTERNAL_SIZE_T nb, size_t pagesize, int extra_flags)
   set_prev_size (p, padding);
   set_head (p, (size - padding) | IS_MMAPPED);
 
+  /* Must also check whether huge pages were used in the mmap call
+     and this is not the fallback call after using huge pages failed */
+  if (__glibc_unlikely (extra_flags & mp_.hp_flags))
+    set_mmap_is_hp (p);
+
   /* update statistics */
   int new = atomic_fetch_add_relaxed (&mp_.n_mmaps, 1) + 1;
   atomic_max (&mp_.max_n_mmaps, new);
@@ -2982,8 +3011,8 @@ munmap_chunk (mchunkptr p)
   assert (chunk_is_mmapped (p));
 
   uintptr_t mem = (uintptr_t) chunk2mem (p);
-  uintptr_t block = (uintptr_t) p - prev_size (p);
-  size_t total_size = prev_size (p) + size;
+  uintptr_t block = (uintptr_t) p - prev_size_mmap (p);
+  size_t total_size = prev_size_mmap (p) + size;
   /* Unfortunately we have to do the compilers job by hand here.  Normally
      we would test BLOCK and TOTAL-SIZE separately for compliance with the
      page size.  But gcc does not recognize the optimization possibility
@@ -3007,8 +3036,8 @@ munmap_chunk (mchunkptr p)
 static mchunkptr
 mremap_chunk (mchunkptr p, size_t new_size)
 {
-  size_t pagesize = GLRO (dl_pagesize);
-  INTERNAL_SIZE_T offset = prev_size (p);
+  size_t pagesize = mmap_is_hp (p) ? mp_.hp_pagesize : GLRO (dl_pagesize);
+  INTERNAL_SIZE_T offset = prev_size_mmap (p);
   INTERNAL_SIZE_T size = chunksize (p);
   char *cp;
 
@@ -3040,7 +3069,7 @@ mremap_chunk (mchunkptr p, size_t new_size)
 
   assert (!misaligned_chunk (p));
 
-  assert (prev_size (p) == offset);
+  assert (prev_size_mmap (p) == offset);
   set_head (p, (new_size - offset) | IS_MMAPPED);
 
   INTERNAL_SIZE_T new;
-- 
2.43.0



More information about the Libc-alpha mailing list