memfd_create and Anonymous Memory Files
memfd_create(name, flags)creates a brand-new, nameless file that lives entirely in RAM — backed bytmpfs(orhugetlbfs) — and returns an open file descriptor to it. The file has no path in any directory, is never written to disk, and is automatically destroyed when the last open reference is closed (the descriptor, plus anymmapmappings and any copies passed to other processes). Because it is just a file you canftruncateto a size,mmapto share, and pass to another process over a Unix-domain socket, it has become the modern, clean replacement for the old “open a/dev/shmfile, thenunlinkit immediately” dance for making a shared memory buffer. It first appeared in Linux 3.17 (October 2014), authored by David Herrmann, and its companion feature — file sealing — is what makes it more than a convenience (Herrmann’s announcement; memfd_create(2)).
Mental Model
Think of a memfd as a tmpfs file with no name. The kernel’s tmpfs (the in-RAM filesystem also used for /dev/shm and /tmp on many systems) can hold files; ordinarily a file gets there by being created at a path. memfd_create short-circuits the path entirely: it calls shmem_file_setup() directly to build a fresh tmpfs inode and hands you a file descriptor without ever inserting the file into any directory tree. There is no name to collide with, no mountpoint to find, and no cleanup to forget — when every reference to that inode is gone, the inode and its pages are freed, exactly like any anonymous resource.
The name you pass is purely cosmetic: it shows up only in /proc/<pid>/fd/<fd> as a symlink target of the form /memfd:<name> (deleted), used for debugging and for tools like lsof. Two memfds created with the same name are completely independent files; the name is not a key, not a handle, and not visible to other processes except through your /proc entry.
flowchart LR A["Process A:<br/>fd = memfd_create('buf', MFD_CLOEXEC)"] --> B["nameless tmpfs inode<br/>(in RAM, 0 bytes)"] B --> C["ftruncate(fd, size)<br/>mmap(fd) -- fill data"] C --> D["sendmsg over AF_UNIX<br/>SCM_RIGHTS: pass the fd"] D --> E["Process B:<br/>receives a NEW fd to<br/>the SAME inode"] E --> F["mmap(fd) -- sees the<br/>identical physical pages"] F --> G["all fds closed +<br/>all mappings gone<br/>-> inode freed"]
How a memfd travels between two processes. What it shows: the buffer is created without any filesystem path, populated via mmap, and the file descriptor (not a name) is handed to a second process through a Unix socket’s SCM_RIGHTS ancillary data; the recipient gets its own descriptor referring to the same in-RAM inode and maps the identical physical pages. The insight to take: the memfd’s identity is the open file description itself, so lifetime is reference-counted across processes — nobody has to remember to unlink anything, and the buffer disappears the instant the last holder lets go. The fd-passing step is owned by Passing File Descriptors with SCM_RIGHTS; the buffer-handoff protocol by Passing memfd Buffers Between Processes.
Why memfd Replaced the Old Dances
Before memfd_create, a process that wanted an anonymous, shareable RAM buffer had three awkward options, each with a real defect (Herrmann 2014):
-
open()a file in/tmp, thenunlink()it. This needs a writable filesystem mount, leaves a window betweenopenandunlinkwhere the file is visible (and can be opened by a racing process or left behind if you crash in between), and on a real disk-backed/tmpthe data can hit storage. -
shm_open()on/dev/shm, thenshm_unlink(). This is the POSIX shared-memory idiom (see POSIX Shared Memory and shm_open). It is RAM-backed viatmpfs, but it still requires a global name in a global namespace (/dev/shm/<name>), so two unrelated programs can collide on the same name, and a crash betweenshm_openandshm_unlinkleaks a named object that survives in/dev/shmuntil someone cleans it up. -
open()withO_TMPFILE. This creates an unnamed temporary file directly, avoiding the unlink race — but it still needs a mountpoint that supportsO_TMPFILE, and it does not provide sealing.
memfd_create removes the mountpoint requirement, the global namespace, and the cleanup race in one stroke: “memfd_create does not require a local mount-point… there are no name-clashes and no global registry” (Herrmann 2014). The buffer is anonymous from birth, and its lifetime is automatic. But the decisive advantage is that a memfd created with MFD_ALLOW_SEALING can be sealed so a recipient can trust it — something none of the older approaches can offer. That trust model is the whole subject of File Sealing with F_ADD_SEALS.
Mechanical Walk-through
The system call is int memfd_create(const char *name, unsigned int flags), declared in <sys/mman.h> under _GNU_SOURCE. Its kernel implementation lives in mm/memfd.c as SYSCALL_DEFINE2(memfd_create, ...) (v6.12 mm/memfd.c). Walking the 6.12 source, the syscall does the following:
-
Validate flags. If
MFD_HUGETLBis not set, any bit outsideMFD_ALL_FLAGS(which isMFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB | MFD_NOEXEC_SEAL | MFD_EXEC) yields-EINVAL. IfMFD_HUGETLBis set, the huge-page-size encoding bits (MFD_HUGE_MASK << MFD_HUGE_SHIFT) are additionally allowed. It is also-EINVALto set bothMFD_EXECandMFD_NOEXEC_SEALat once, since they are opposites. -
Apply the noexec sysctl policy.
check_sysctl_memfd_noexec(&flags)consults the per-PID-namespacevm.memfd_noexecsetting (covered below) and may auto-addMFD_NOEXEC_SEALorMFD_EXEC, or reject the call with-EACCES. -
Copy and bound the name.
strnlen_user()reads the user string; it must be at mostMFD_NAME_MAX_LENbytes, which isNAME_MAX(255) minus the length of the"memfd:"prefix — i.e. a 249-byte limit (v6.12 mm/memfd.c). The kernel prepends"memfd:"to form the displayed name. A name longer than the limit is-EINVAL; a bad pointer is-EFAULT. -
Allocate the fd.
get_unused_fd_flags(MFD_CLOEXEC ? O_CLOEXEC : 0)reserves a descriptor with the close-on-exec flag if requested. -
Create the backing file. For
MFD_HUGETLB, it callshugetlb_file_setup(...)to back the file with huge pages fromhugetlbfs; otherwise it callsshmem_file_setup(name, 0, VM_NORESERVE)to make a zero-lengthtmpfs-backed file.VM_NORESERVEmeans no swap space is pre-reserved — pages are accounted as they are actually used. The file’s mode getsFMODE_LSEEK | FMODE_PREAD | FMODE_PWRITEandO_LARGEFILE, so it supports seeking and positional I/O and large sizes. -
Set default seals. If
MFD_NOEXEC_SEALwas set, the kernel clears the file’s execute bits (inode->i_mode &= ~0111) and stamps the inode’s seal set withF_SEAL_EXEC, while clearing the defaultF_SEAL_SEAL. If insteadMFD_ALLOW_SEALINGwas set, it clearsF_SEAL_SEALso seals can be added later. If neither flag is set, the file keeps the implicitF_SEAL_SEALthattmpfs/hugetlbfsinodes carry by default, which is why an ordinary memfd silently rejects all sealing attempts — see File Sealing with F_ADD_SEALS. -
Install and return.
fd_install(fd, file)publishes the descriptor,nameis freed, and the fd is returned.
The returned descriptor starts at size 0; you must ftruncate(fd, n) (or write to it) before mapping n bytes, or your mmap will succeed but touching beyond the file’s end faults with SIGBUS. After sizing, you mmap(NULL, n, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0) to get a shared view backed by the file’s pages; a write through that mapping is immediately visible through any other mapping of the same inode in any process, which is the entire point.
The v6.18 LTS source is functionally identical to v6.12 for these paths; the only differences are a comment typo fix and an internal is_write_sealed() helper plus refactored huge-page allocation accounting (verified by diffing v6.12 against v6.18 mm/memfd.c). The flag bit values are unchanged between the two LTS lines.
The Flags, One by One
The flag bit values are defined in include/uapi/linux/memfd.h (v6.12):
-
MFD_CLOEXEC(0x0001) — setsO_CLOEXEC(close-on-exec) on the returned descriptor, so it is not inherited across anexecve(2). As with all fds, you almost always want this unless you are deliberately handing the descriptor to a child program; leaking an unsealed, writable memfd acrossexecis a footgun. -
MFD_ALLOW_SEALING(0x0002) — permits laterfcntl(fd, F_ADD_SEALS, ...)calls by clearing the defaultF_SEAL_SEAL. Without this flag, the file is born sealed-against-sealing and no seals can ever be added. This is the gate to the entire sealing feature; see File Sealing with F_ADD_SEALS. -
MFD_HUGETLB(0x0004, since Linux 4.14) — backs the file with explicit huge pages fromhugetlbfsrather than ordinarytmpfspages. Useful for very large shared buffers where reducing TLB (translation lookaside buffer) pressure matters, e.g. databases or virtual-machine memory. The huge-page size is selected by OR-ing in one of theMFD_HUGE_*encodings (MFD_HUGE_2MB,MFD_HUGE_1GB, and others) shifted into the high bits; the size must be one the hardware and kernel actually support. Note that in older kernelsMFD_HUGETLBwas mutually exclusive withMFD_ALLOW_SEALING; sealing support for hugetlbfs memfds was added later (the 6.12F_ALL_SEALSpath covers bothshmem_fileandis_file_hugepagesinodes viamemfd_file_seals_ptr).
Uncertain
Verify: the exact kernel release in which
MFD_HUGETLBmemfds became sealable (i.e. when the oldMFD_HUGETLB+MFD_ALLOW_SEALING-EINVALrestriction was lifted). Reason: the v6.12 source clearly supports seals on hugetlbfs inodes viaHUGETLBFS_I(...)->seals, but I did not pin the introducing version against a primary changelog. To resolve: bisect thememfd_file_seals_ptr()/ hugetlbfs-seal commit history or check thefcntl/memfd_createman-page CHANGES. uncertain
-
MFD_NOEXEC_SEAL(0x0008, since Linux 6.3) — creates the memfd without the execute bit (mode0666instead of0777) and seals it withF_SEAL_EXECso it can never bechmod-ed back to executable (LWN 917910; v6.12 mm/memfd.c). This is the secure default for buffers that should hold data, not code. -
MFD_EXEC(0x0010, since Linux 6.3) — explicitly requests the historical behaviour: a memfd created executable (mode0777), suitable for legitimately loading code (some language runtimes andruncuse this). It exists so that, after 6.3, programs can declare their intent rather than relying on the silent default, and so the kernel can warn or refuse when neither flag is given.
The reason MFD_NOEXEC_SEAL / MFD_EXEC were introduced in 6.3 is a real attack class: historically every memfd was created executable, and the syscall offered no way to change that. On a hardened, “write-xor-execute” (W^X) system such as ChromeOS — where all legitimate executables come from a verified-boot read-only root filesystem — an attacker who can write to a memfd and then execve it gains an arbitrary-code-execution / noexec-bypass primitive: a “confused-deputy” path around the policy that all code must come from verified storage (LWN 917910). Making memfds non-executable-and-sealed by policy closes that door.
The vm.memfd_noexec sysctl
To roll this out without breaking existing software, the noexec behaviour is governed by a per-PID-namespace sysctl, vm.memfd_noexec, with three levels (kernel.org mfd_noexec docs):
0(MEMFD_NOEXEC_SCOPE_EXEC) — amemfd_createcall that passes neither flag behaves as ifMFD_EXECwas set (the old executable default).1(MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL) — such a call behaves as ifMFD_NOEXEC_SEALwas set (non-executable, sealed).2(MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED) — such a call is rejected with-EACCES, forcing every caller to be explicit and forbidding executable memfds entirely.
The setting is hierarchical and one-way restrictive: a child PID namespace inherits the value at creation and the kernel searches from the current namespace up to the root, applying the most restrictive setting found; a child can tighten but not relax it (kernel.org docs). This is what check_sysctl_memfd_noexec() in mm/memfd.c enforces.
Worked Example
The following program creates a sealed shared buffer end to end. Each line is annotated.
#define _GNU_SOURCE
#include <sys/mman.h> /* memfd_create, mmap */
#include <unistd.h> /* ftruncate, close */
#include <fcntl.h> /* fcntl, F_ADD_SEALS */
#include <string.h>
#include <stdio.h>
int main(void) {
/* Create a nameless RAM file. MFD_CLOEXEC: don't leak it across exec.
MFD_ALLOW_SEALING: we intend to seal it later. */
int fd = memfd_create("shared-buf", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd < 0) { perror("memfd_create"); return 1; }
/* The file starts at size 0; size it to one page (4096 bytes). */
if (ftruncate(fd, 4096) < 0) { perror("ftruncate"); return 1; }
/* Map it shared+writable and fill it with data. */
char *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED) { perror("mmap"); return 1; }
strcpy(p, "hello from a memfd");
/* CRITICAL: drop the writable mapping before sealing writes.
F_SEAL_WRITE fails with EBUSY while a writable shared mapping lives. */
munmap(p, 4096);
/* Freeze the buffer: no growing, no shrinking, no writing, and no
further seals. A recipient that maps this fd can now trust it. */
if (fcntl(fd, F_ADD_SEALS,
F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_WRITE | F_SEAL_SEAL) < 0) {
perror("F_ADD_SEALS"); return 1;
}
/* fd is now a read-only, fixed-size, trustable buffer.
Pass it to another process over an AF_UNIX socket (SCM_RIGHTS),
or hand it to a child. When the last reference closes, the
inode and its single page are freed automatically. */
close(fd);
return 0;
}The crucial subtlety is the munmap before the F_SEAL_WRITE: as the kernel’s memfd_add_seals shows, adding F_SEAL_WRITE calls mapping_deny_writable(), which fails if any writable shared mapping currently exists, and the seal attempt returns -EBUSY (v6.12 mm/memfd.c). The full seal semantics are the subject of File Sealing with F_ADD_SEALS.
You can inspect the file from the shell while the program runs: ls -l /proc/<pid>/fd/<fd> shows a symlink to /memfd:shared-buf (deleted), and cat /proc/<pid>/maps shows the mapping backed by that anonymous inode.
Failure Modes and Common Misunderstandings
-
mmapsucceeds but access faults withSIGBUS. A fresh memfd is 0 bytes. If yoummap4096 bytes of a 0-byte memfd and then dereference the pointer, you read past end-of-file and getSIGBUS, notSIGSEGV. The fix is toftruncateto the intended size first (orwriteto extend it). This is the single most common beginner mistake. -
fcntl(F_ADD_SEALS)returnsEINVALon a memfd you “definitely” created sealable. Almost always you forgotMFD_ALLOW_SEALING(or, on 6.3+, the file gotF_SEAL_EXEC-sealed viaMFD_NOEXEC_SEALand you are now blocked byF_SEAL_SEALbecause… no —MFD_NOEXEC_SEALclearsF_SEAL_SEAL). WithoutMFD_ALLOW_SEALING, the inode keeps its defaultF_SEAL_SEALand every seal addition fails.memfd_file_seals_ptr()returns the inode’s seal word only fortmpfs/hugetlbfsinodes; on any other fd, sealing operations return-EINVAL. -
F_SEAL_WRITEreturnsEBUSY. A writable, shared mapping of the file still exists somewhere — in your process or any process holding the fd. You mustmunmapall writable shared maps first. There is also a subtle case: outstanding kernel pins fromget_user_pages()(direct I/O, AIO, GUP by drivers) hold extra page references;memfd_wait_for_pins()waits up to roughly 150 ms for them to drain and returns-EBUSYif they do not (v6.12 mm/memfd.c). -
memfd_createreturnsEINVALon an old kernel. The syscall is only present from 3.17, and individual flags from later releases (MFD_HUGETLB4.14;MFD_NOEXEC_SEAL/MFD_EXEC6.3). Passing a flag the running kernel does not know yields-EINVAL. There is no libc fallback; you must detect support at runtime. -
memfd_createreturnsEACCESunexpectedly on 6.3+. Some namespace up the tree setvm.memfd_noexec=2, so you must passMFD_NOEXEC_SEAL(orMFD_EXECif you truly need executable, which value2forbids). This bit several container runtimes when distributions shipped 6.5+ kernels with stricter defaults; programs that calledmemfd_createwith no exec flag started seeing kernel warnings orEACCES. -
Memory accounting and
vm.max_map_count. A memfd’s pages count against the creating process’s memory and, because they aretmpfs, against swap if swapped. Very large memfds can hitRLIMIT_ASonmmapor the system’s memory limits; there is no on-disk overflow.
Alternatives and When to Choose Them
-
POSIX Shared Memory and shm_open (
shm_open+mmap). Choose this when you need a named rendezvous: an unrelated process that knows the agreed name/foocan open the same object without anyone passing a descriptor. memfd is anonymous, so it requires an fd-passing channel (a Unix socket or inheritance) to reach another process. The trade-off is that named POSIX shm has the cleanup/leak and name-collision problems memfd was designed to avoid. -
Anonymous Shared Memory (
mmap(MAP_SHARED|MAP_ANONYMOUS)). Choose this when only related processes need the buffer: an anonymous shared mapping is inherited acrossforkbut has no file descriptor, so it cannot be passed to an unrelated process and cannot be sealed. memfd gives you a descriptor and sealing at the cost of one syscall. -
System V shared memory (
shmget/shmat). Legacy; kernel-persistent objects keyed by an integer that survive process death and leak unlessipcrm-ed. Avoid in new code; memfd is strictly cleaner. -
open(O_TMPFILE)//dev/shmopen-then-unlink. The historical approaches memfd replaces; choose them only on pre-3.17 kernels.
The decisive reason to choose memfd over all of these for a modern shared-buffer protocol is sealing: only a sealed memfd lets a recipient cryptographically-style trust that the buffer will not change size or content underneath it. That is why it underpins Wayland graphics buffers, sandboxed media decoders, and dma-buf heap handoffs — covered in File Sealing with F_ADD_SEALS and Passing memfd Buffers Between Processes.
Production Notes
memfd is everywhere in modern Linux userspace. Wayland compositors and clients exchange wl_shm pool buffers as sealed memfds so the compositor can map an untrusted client’s buffer without defensive SIGBUS handling (Herrmann 2014). Android migrated its ashmem shared-memory driver onto memfd + sealing, which is what drove the addition of F_SEAL_FUTURE_WRITE in Linux 5.1 (Fernandes patch). systemd and various daemons use memfd for passing logs and credentials. Container runtimes (runc, crun) copy the runtime binary into a memfd before re-exec to defend against a compromised container overwriting the host binary — ironically one of the legitimate uses of MFD_EXEC, and a reason value 2 of vm.memfd_noexec can break naive runtimes. The flip side — memfd as an attacker tool for fileless code execution (write a payload to a memfd, execve it, leave nothing on disk) — is precisely why MFD_NOEXEC_SEAL and the noexec sysctl were added; security tooling now watches for memfd: entries in /proc/<pid>/maps as an indicator of in-memory malware.
See Also
- File Sealing with F_ADD_SEALS — the seals (
F_SEAL_WRITE/SHRINK/GROW/SEAL/FUTURE_WRITE/EXEC) that turn a memfd into a trustable buffer - Passing memfd Buffers Between Processes — the fd-passing handoff protocol over Unix sockets
- Passing File Descriptors with SCM_RIGHTS — the
SCM_RIGHTSancillary-data mechanism a memfd rides on - POSIX Shared Memory and shm_open — the named-object alternative memfd improves on
- Anonymous Shared Memory —
MAP_ANONYMOUS|MAP_SHAREDfor related processes, with no fd and no sealing - Shared Memory via mmap — the general page-table-sharing mechanism memfd plugs into
- Unix-Domain Sockets — the local socket that carries the memfd to another process
- Linux IPC MOC — parent map (§9 memfd and File Sealing)