Linux - memfd
In Linux, memfd (specifically created via the memfd_create system call) is an anonymous file that lives entirely in RAM.
Unlike standard files, a memfd has no link to a physical disk and no path in the global filesystem. It exists only as a File Descriptor (FD). When all references to the file descriptor are closed, the kernel automatically reclaims the memory.
Key Characteristics of memfd
- RAM-Backed (tmpfs): Under the hood,
memfdis backed by the kernel's virtual memory subsystem (tmpfs). It is extremely fast and page-cache aligned. - Anonymous: It does not exist in
/tmpor/dev/shmor any directory tree. It is only identifiable via/proc/[pid]/fd/[num]which points to/memfd:<name> (deleted). - FD-Bound Lifetime: The file is completely ephemeral. Once the last process holding its file descriptor closes it (or exits), the file ceases to exist.
- Sealing Support: A unique security mechanism that allows locking a file's state (size, content) to prevent further modifications.
Core Features & Use Cases
1. Secure Inter-Process Communication (IPC)
If Process A wants to share a large block of memory with Process B:
- Historically: One would use POSIX shared memory (
shm_open) or create a temp file in/dev/shm. However, any other process on the system could discover the file path and attempt to read or corrupt it. - Using
memfd: Process A callsmemfd_create(), writes the data, and passes the resulting file descriptor to Process B over a Unix Domain Socket usingSCM_RIGHTS. No other processes can guess a filename or access the memory.
2. File Sealing (Memory Safety)
When sharing memory with untrusted or sandbox processes, you want to guarantee they cannot corrupt or resize the shared buffer (which could crash the host process).
memfd supports sealing via fcntl(). Once a seal is applied, the kernel enforces the constraint:
F_SEAL_SHRINK: The file size cannot be reduced.F_SEAL_GROW: The file size cannot be increased.F_SEAL_WRITE: The file contents cannot be written to.F_SEAL_FUTURE_WRITE: Prevents write access through new mappings, but permits writes via existing writable memory mappings.F_SEAL_SEAL: Prevents any further seals from being added.
3. Fileless Execution (Binary Loading)
Security software, container runtimes, and loaders use memfd to execute binaries directly from RAM without writing them to disk.
By copying an executable binary into a memfd and passing its FD to fexecve() or execveat(), Linux executes the binary directly out of memory. This leaves no footprints in the physical disk filesystems.
Code Example: Creating, Sealing, and Sharing Memory
Below is a C example demonstrating how to create a memfd, write to it, seal it to prevent further modifications, and prepare it for sharing.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
int main() {
// 1. Create an anonymous memfd
int fd = memfd_create("my_shared_buffer", MFD_ALLOW_SEALING);
if (fd == -1) {
perror("memfd_create");
exit(EXIT_FAILURE);
}
// 2. Set the initial size
if (ftruncate(fd, 4096) == -1) {
perror("ftruncate");
close(fd);
exit(EXIT_FAILURE);
}
// 3. Write data to the RAM file
write(fd, "Hello from memory!", 18);
// 4. Apply seals (prevent shrinking, growing, and writing)
if (fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) == -1) {
perror("fcntl F_ADD_SEALS");
close(fd);
exit(EXIT_FAILURE);
}
printf("memfd created, populated, and sealed successfully!\n");
// 5. Subsequent writes will fail
if (write(fd, "Failure test", 12) == -1) {
perror("Expected write failure"); // This will print "Permission denied"
}
close(fd);
return 0;
}
Code Example: Fileless Execution (Running from RAM)
Here is how you can use memfd_create and fexecve to execute a program (like /bin/ls) directly from memory without writing it to disk.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
// 1. Open the target executable binary on disk (just to read it into memory)
int source_fd = open("/bin/ls", O_RDONLY);
if (source_fd == -1) {
perror("open /bin/ls");
exit(EXIT_FAILURE);
}
// 2. Create the memfd to hold the binary in RAM
int mem_fd = memfd_create("run_from_ram", MFD_CLOEXEC);
if (mem_fd == -1) {
perror("memfd_create");
close(source_fd);
exit(EXIT_FAILURE);
}
// 3. Copy the binary contents from disk into the memfd
char buffer[4096];
ssize_t bytes_read;
while ((bytes_read = read(source_fd, buffer, sizeof(buffer))) > 0) {
write(mem_fd, buffer, bytes_read);
}
close(source_fd);
// 4. Execute the binary directly from the memory file descriptor
char *argv[] = { "ls", "-la", NULL };
char *envp[] = { NULL };
printf("Executing 'ls' directly from RAM...\n");
fexecve(mem_fd, argv, envp);
// If fexecve succeeds, this point is never reached
perror("fexecve failed");
close(mem_fd);
return EXIT_FAILURE;
}
Comparison: memfd vs. shm_open vs. /dev/shm
| Feature | memfd_create |
shm_open (POSIX SHM) |
/dev/shm (tmpfs file) |
|---|---|---|---|
| Lookup Method | File descriptor only (anonymous) | Global name in filesystem namespace | Regular file path in /dev/shm |
| Creation Namespace | Private to process | Global system-wide namespace | Directory path |
| Access Control | Handed via descriptor sharing (secure) | Accessible to any process that knows the name | Respects file permission bits |
| File Sealing | Yes (prevent writes/resizes) | No | No |
| Lifetime | Reclaimed when last FD closes | Persists until explicitly unlinked | Persists until deleted or rebooted |
| Portability | Linux-specific (3.17+) | POSIX standard | POSIX standard (Linux/BSD) |