mremap(2)

๐Ÿ’– mremap(2) โ€” remap a virtual memory address ๐Ÿ’–

okay bestie, settle in, because this is the System Calls Manual entry for mremap(2) and she is lowkey a whole icon. it's a standard C library thing (libc, -lc), she lives in Linux, and she is about to let you resize a chunk of memory without copying a single byte. no cap. ๐ŸŒˆ


โœจ NAME โœจ

mremap โ€” remap a virtual memory address

๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library (libc, -lc)

๐Ÿซถ SYNOPSIS ๐Ÿซถ

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <sys/mman.h>

void *mremap(size_t old_size;
             void old_address[old_size], size_t old_size,
             size_t new_size, int flags, ...  /* void *new_address */);

(yeah that prototype is a little cursed, she's built different, we love her anyway ๐Ÿ’…)

๐ŸŒธ DESCRIPTION ๐ŸŒธ

mremap() expands (or shrinks) a memory mapping you already have, and โ€” plot twist โ€” she might move it somewhere else at the same time. that whole relocation situation is controlled by the flags argument and by how much virtual address space is actually free. ๐Ÿฆ‹

Mappings can also just be moved with no resizing at all: you pass equal old_size and new_size and throw in the MREMAP_FIXED flag (details below, keep reading bestie). Since Linux 6.17, when you're doing a simple move, old_address has to be mapped, but old_size is allowed to span multiple mappings โ€” including unmapped gaps sitting between them. you can also drop MREMAP_DONTUNMAP in there.

Similarly, if the operation is a shrink โ€” meaning old_size is bigger than new_size โ€” then old_size is also allowed to span multiple mappings, and those mappings don't even have to be next to each other, no adjacency required. if that shrink happens in-place, meaning neither MREMAP_FIXED nor MREMAP_DONTUNMAP is specified, then new_size may span multiple VMAs too. however ๐Ÿ”ฅ if the range is moved, then new_size has to span only a single mapping. rules are rules.

If the operation is neither a MREMAP_FIXED move nor a shrink, then old_size must span only a single mapping. periodt.

  • old_address โ€” the old address of the first virtual memory block you wanna expand, shrink, and/or move. โš ๏ธ note: old_address has to be page aligned.
  • old_size โ€” the size of the range containing the virtual memory blocks being manipulated.
  • new_size โ€” the requested size of the virtual memory blocks after the resize.
  • new_address โ€” an optional fifth argument, may be provided; see the MREMAP_FIXED description below.

If the value of old_size is zero, and old_address refers to a shareable mapping (see the description of MAP_SHARED in mmap(2)), then mremap() will create a new mapping of the same pages. new_size becomes the size of that new mapping, and you can specify where it goes with new_address (see MREMAP_FIXED below). ๐ŸŽ€ if you request a new mapping this way, the MREMAP_MAYMOVE flag must also be specified.

The flags bit-mask argument may be 0, or it may include the following flags:

  • MREMAP_MAYMOVE โ€” by default, if there isn't enough space to expand a mapping right where it currently sits, mremap() just fails, sad. if this flag is specified, the kernel is permitted to relocate the mapping to a new virtual address if that's what it takes. โš ๏ธ if the mapping gets relocated, then absolute pointers into the old mapping location become invalid โ€” use offsets relative to the starting address of the mapping instead, bestie, i'm begging you.
  • MREMAP_FIXED (since Linux 2.3.31) โ€” this flag does a similar job to the MAP_FIXED flag of mmap(2). specify it and mremap() accepts a fifth argument, void *new_address, which specifies a page-aligned address the mapping must be moved to. any previous mapping in the address range specified by new_address and new_size gets unmapped. ๐Ÿ’€
    • If MREMAP_FIXED is specified, then MREMAP_MAYMOVE must also be specified.
    • Since Linux 6.17, if old_size equals new_size and MREMAP_FIXED is specified, then old_size may span beyond the mapping in which old_address resides. in that case, gaps between mappings in the original range are maintained in the new range. the whole operation is performed atomically unless an error arises โ€” and if an error does arise, the operation may be partially completed, i.e. some mappings moved and others not. ๐Ÿ™ˆ
    • Moving multiple mappings is not permitted if any of those mappings have either been registered with userfaultfd(2), or map drivers that specify their own custom address mapping logic.
  • MREMAP_DONTUNMAP (since Linux 5.7) โ€” this flag, which must be used together with MREMAP_MAYMOVE, remaps mappings to a new address but does not unmap them from their original address. she duplicates and dips. โœจ
    • the MREMAP_DONTUNMAP flag can only be used with mappings that are not VM_DONTEXPAND or VM_PFNMAP. before Linux 5.13, it could only be used with private anonymous mappings (see the description of MAP_PRIVATE and MAP_ANONYMOUS in mmap(2)).
    • after completion, any access to the range specified by old_address and old_size will result in a page fault. that page fault gets handled by a userfaultfd(2) handler if the address sits in a range previously registered with userfaultfd(2). otherwise, the kernel just allocates a zero-filled page to handle the fault.
    • the MREMAP_DONTUNMAP flag may be used to atomically move a mapping while leaving the source mapped. see NOTES for some possible applications. ๐Ÿ‘‘

If the memory segments specified by old_address and old_size are locked (using mlock(2) or similar), then that lock is maintained when the segments get resized and/or relocated. as a consequence, the amount of memory locked by the process may change. ๐Ÿซถ

โญ RETURN VALUE โญ

On success, mremap() returns a pointer to the new virtual memory area. ๐ŸŒŸ

On error, the value MAP_FAILED is returned โ€” that's (void *) -1 โ€” and errno is set to indicate the error.

๐Ÿ’€ ERRORS ๐Ÿ’€

  • EAGAIN โ€” the caller tried to expand a memory segment that is locked, but that wasn't possible without blowing past the RLIMIT_MEMLOCK resource limit.
  • EFAULT โ€” some address in the range old_address to old_address+old_size is an invalid virtual memory address for this process. you can also get EFAULT even if there exist mappings covering the whole address space you asked for, but those mappings are of different types, and the mremap() operation being performed doesn't support that. ๐Ÿ˜ญ
  • EINVAL โ€” an invalid argument was given. possible causes are:
    • old_address was not page aligned;
    • a value other than MREMAP_MAYMOVE or MREMAP_FIXED or MREMAP_DONTUNMAP was specified in flags;
    • new_size was zero;
    • new_size or new_address was invalid;
    • the new address range specified by new_address and new_size overlapped the old address range specified by old_address and old_size;
    • MREMAP_FIXED or MREMAP_DONTUNMAP was specified without also specifying MREMAP_MAYMOVE;
    • MREMAP_DONTUNMAP was specified, but one or more pages in the range specified by old_address and old_size were not private anonymous;
    • MREMAP_DONTUNMAP was specified and old_size was not equal to new_size;
    • old_size was zero and old_address does not refer to a shareable mapping (but see BUGS);
    • old_size was zero and the MREMAP_MAYMOVE flag was not specified.
  • ENOMEM โ€” not enough memory was available to complete the operation. possible causes are:
    • the memory area cannot be expanded at the current virtual address, and the MREMAP_MAYMOVE flag is not set in flags. or, there is not enough (virtual) memory available.
    • MREMAP_DONTUNMAP was used, causing a new mapping to be created that would exceed the (virtual) memory available. or, it would exceed the maximum number of allowed mappings.

๐ŸŒˆ STANDARDS ๐ŸŒˆ

Linux. ๐Ÿง (she's exclusive like that)

๐ŸŽ€ HISTORY ๐ŸŽ€

Before glibc 2.4, glibc did not expose the definition of MREMAP_FIXED, and the prototype for mremap() did not allow for the new_address argument. throwback era, we don't miss it. ๐Ÿ™ˆ

๐Ÿ‘‘ NOTES ๐Ÿ‘‘

mremap() changes the mapping between virtual addresses and memory pages. this can be used to implement a very efficient realloc(3). slay. ๐Ÿ’…

In Linux, memory is divided into pages. a process has (one or) several linear virtual memory segments. each virtual memory segment has one or more mappings to real memory pages (in the page table). each virtual memory segment has its own protection (access rights), which may cause a segmentation violation (SIGSEGV) if the memory is accessed incorrectly โ€” for example, writing to a read-only segment. accessing virtual memory outside of the segments will also cause a segmentation violation. ๐Ÿ˜ญ

If mremap() is used to move or expand an area locked with mlock(2) or equivalent, the mremap() call will make a best effort to populate the new area, but will not fail with ENOMEM if the area cannot be populated.

๐Ÿฆ‹ MREMAP_DONTUNMAP use cases ๐Ÿฆ‹

Possible applications for MREMAP_DONTUNMAP include:

  • Non-cooperative userfaultfd(2): an application can yank out a virtual address range using MREMAP_DONTUNMAP and then employ a userfaultfd(2) handler to handle the page faults that subsequently occur as other threads in the process touch pages in the yanked range. iconic behaviour, honestly.
  • Garbage collection: MREMAP_DONTUNMAP can be used together with userfaultfd(2) to implement garbage collection algorithms (e.g., in a Java virtual machine). such an implementation can be cheaper (and simpler) than conventional garbage collection techniques that involve marking pages with protection PROT_NONE in conjunction with the use of a SIGSEGV handler to catch accesses to those pages. ate. ๐Ÿ”ฅ

๐Ÿ’œ BUGS ๐Ÿ’œ

Before Linux 4.14, if old_size was zero and the mapping referred to by old_address was a private mapping (see the description of MAP_PRIVATE in mmap(2)), mremap() created a new private mapping unrelated to the original mapping.

this behavior was unintended and probably unexpected in user-space applications (since the whole intention of mremap() is to create a new mapping based on the original mapping). since Linux 4.14, mremap() fails with the error EINVAL in this scenario. glow-up complete. โœจ

๐Ÿ“ SEE ALSO ๐Ÿ“

brk(2), getpagesize(2), getrlimit(2), mlock(2), mmap(2), sbrk(2), malloc(3), realloc(3)

your favorite text book on operating systems for more information on paged memory (e.g., Modern Operating Systems by Andrew S. Tanenbaum, Inside Linux by Randolph Bentson, The Design of the UNIX Operating System by Maurice J. Bach) ๐Ÿ“–


and that's the whole page bestie!! ๐Ÿฅณ this one came straight out of Linux man-pages 6.20, dated 2026-02-08 โ€” so if you're reading this, shoutout to the man-pages girlies keeping mremap(2) immaculate for us. now go forth and remap responsibly, mwah ๐Ÿ’–๐ŸŽ€๐Ÿซถ

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: GPL-2.0-or-later
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms