# 34. memfd — 메모리 기반 파일 디스크립터
memfd는 tmpfs 또는 hugetlbfs 위에 익명 메모리 파일을 생성하는 시스템 호출이다. memfd_create()로 생성된 파일 디스크립터는 unnamed memory object로, 파일 시스템에 마운트되지 않지만 일반 파일처럼 mmap(), read(), write()가 가능하다. 주요 사용 사례로는 IPC, 세이프 샌드박스, SECCOMP 필터와의 조합 등이 있다.
memfd의 핵심 기능은 파일 봉인(File Sealing)이다. 여러 프로세스가 익명 메모리를 공유할 때, 상대방이 수행할 수 있는 연산을 제한하는 메커니즘이다. 봉인은 추가만 가능하고 제거할 수 없으므로, 서로 신뢰하지 않는 프로세스 사이에서도 안전한 메모리 공유가 가능하다.
호출 흐름:
자료구조 관계:
소스 파일 경로:
mm/memfd.c ← memfd_create 시스템 호출, 봉인 관리
include/linux/memfd.h ← memfd API 선언
include/linux/shmem_fs.h ← shmem_inode_info (봉인 저장)
include/linux/hugetlb.h ← hugetlbfs_inode_info (봉인 저장)
include/uapi/linux/memfd.h ← MFD_* 플래그 정의
include/uapi/linux/fcntl.h ← F_SEAL_* 플래그 정의
# memfd 파일 디스크립터 확인
ls -la /proc/<pid>/fd | grep memfd
# memfd 파일 정보 확인
cat /proc/<pid>/fdinfo/<fd>
# 시스템 전체 memfd 카운트 확인
cat /proc/sys/fs/memfd_noexec # noexec 범위 확인
# tmpfs 마운트 확인 (memfd의 기본 백엔드)
mount | grep tmpfs
# HugeTLBFS 마운트 확인 (MFD_HUGETLB용)
mount | grep hugetlb
# memfd 시스템 호출 호출 수 확인
cat /proc/kallsyms | grep sys_memfd_create
# 현재 프로세스의 memfd 열기
python3 -c "import os; fd = os.memfd_create('test', 0); print(fd); input()"
# memfd 봉인 상태 확인 (Python)
python3 -c "
import fcntl, os
fd = os.memfd_create('test', os.MFD_ALLOW_SEALING)
print('seals:', fcntl.fcntl(fd, fcntl.F_GET_SEALS))
"
// include/linux/shmem_fs.h:36-59
struct shmem_inode_info {
spinlock_t lock;
unsigned int seals; // memfd 봉인 비트마스크 저장
unsigned long flags;
unsigned long alloced; // 파일에 할당된 데이터 페이지 수
unsigned long swapped; // swap에 할당된 하위 합계
union {
struct offset_ctx dir_offsets;
struct {
struct list_head shrinklist;
struct list_head swaplist;
};
};
struct timespec64 i_crtime; // 파일 생성 시간
struct shared_policy policy; // NUMA 메모리 정책
struct simple_xattrs xattrs;
pgoff_t fallocend;
unsigned int fsflags;
atomic_t stop_eviction;
struct inode vfs_inode;
};
// include/linux/hugetlb.h:519-522
struct hugetlbfs_inode_info {
struct inode vfs_inode;
unsigned int seals; // memfd 봉인 비트마스크 (shmem과 동일 형식)
};
// include/uapi/linux/fcntl.h:47-52
#define F_SEAL_SEAL 0x0001 // 추가 봉인 방지
#define F_SEAL_SHRINK 0x0002 // 파일 축소 방지
#define F_SEAL_GROW 0x0004 // 파일 확장 방지
#define F_SEAL_WRITE 0x0008 // 쓰기 방지
#define F_SEAL_FUTURE_WRITE 0x0010 // 매핑된 상태에서 향후 쓰기 방지
#define F_SEAL_EXEC 0x0020 // exec 비트 변경 방지
// include/uapi/linux/memfd.h:8-14
#define MFD_CLOEXEC 0x0001 // exec 시 닫기
#define MFD_ALLOW_SEALING 0x0002 // 봉인 허용
#define MFD_HUGETLB 0x0004 // HugeTLB 백엔드 사용
#define MFD_NOEXEC_SEAL 0x0008 // 실행 불가 + 봉인
#define MFD_EXEC 0x0010 // 실행 가능
// mm/memfd.c:29-30
#define MEMFD_TAG_PINNED PAGECACHE_TAG_TOWRITE // 추가 참조가 있는 folio 태그
#define LAST_SCAN 4 // 최대 스캔 횟수 (~150ms)
// mm/memfd.c:505-523
SYSCALL_DEFINE2(memfd_create,
const char __user *, uname,
unsigned int, flags)
{
char *name __free(kfree) = NULL;
unsigned int fd_flags;
int error;
error = sanitize_flags(&flags); // 플래그 유효성 검증
if (error < 0)
return error;
name = alloc_name(uname); // "memfd:" 접두사 추가
if (IS_ERR(name))
return PTR_ERR(name);
fd_flags = (flags & MFD_CLOEXEC) ? O_CLOEXEC : 0;
return FD_ADD(fd_flags, memfd_alloc_file(name, flags)); // fd 반환
}
memfd_create("name", flags) 호출 시 진입MFD_CLOEXEC → O_CLOEXEC 설정, MFD_HUGETLB → hugetlbfs 백엔드 선택// mm/memfd.c:458-503
struct file *memfd_alloc_file(const char *name, unsigned int flags)
{
// MFD_HUGETLB이면 hugetlb_file_setup, 아니면 shmem_file_setup
if (flags & MFD_HUGETLB) {
file = hugetlb_file_setup(name, 0, mk_vma_flags(VMA_NORESERVE_BIT),
HUGETLB_ANONHUGE_INODE,
(flags >> MFD_HUGE_SHIFT) & MFD_HUGE_MASK);
} else {
file = shmem_file_setup(name, 0, mk_vma_flags(VMA_NORESERVE_BIT));
}
// 보안 초기화, 파일 모드 설정 (FMODE_LSEEK, FMODE_PREAD 등)
// MFD_NOEXEC_SEAL → exec 비트 제거 + F_SEAL_EXEC 설정
// MFD_ALLOW_SEALING → F_SEAL_SEAL 해제 (봉인 허용)
}
MFD_HUGETLB → HugeTLB 할당, MFD_NOEXEC_SEAL → exec 비트 제거// mm/memfd.c:230-310
static int memfd_add_seals(struct file *file, unsigned int seals)
{
// 쓰기 권한 확인
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
// 기존 SEAL_SEAL이 설정되어 있으면 추가 봉인 불가
if (*file_seals & F_SEAL_SEAL)
return -EPERM;
// SEAL_WRITE 추가 시: 매핑 거부 → pinned folio 대기
if ((seals & F_SEAL_WRITE) && !(*file_seals & F_SEAL_WRITE)) {
error = mapping_deny_writable(file->f_mapping);
error = memfd_wait_for_pins(file->f_mapping);
}
// SEAL_EXEC: exec 비트가 있으면 SHRINK/GROW/WRITE/FUTURE_WRITE도 추가
if (seals & F_SEAL_EXEC && inode->i_mode & 0111)
seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE;
}
F_SEAL_WRITE → mapping_deny_writable + memfd_wait_for_pins, F_SEAL_EXEC → W^X 정책 자동 적용// mm/memfd.c:155-208
static int memfd_wait_for_pins(struct address_space *mapping)
{
// 1단계: 모든 folio 스캔하여 extra ref가 있는 것 태그
memfd_tag_pins(&xas);
// 2단계: 최대 4회 스캔 (지수 백오프)
for (scan = 0; scan <= LAST_SCAN; scan++) {
if (!scan)
lru_add_drain_all(); // 첫 스캔: LRU 플러시
else
schedule_timeout_killable((HZ << scan) / 200); // 백오프 대기
// MEMFD_TAG_PINNED 태그가 남아있으면 계속 대기
// 마지막 스캔에서 still pinned → -EBUSY 반환
}
}
-EBUSY// mm/memfd.c:397-407
int memfd_check_seals_mmap(struct file *file, vm_flags_t *vm_flags_ptr)
{
unsigned int seals = *memfd_file_seals_ptr(file);
if (is_write_sealed(seals))
err = check_write_seal(vm_flags_ptr); // 쓰기 매핑 차단
return err;
}
mmap() 호출 시 write 봉인 여부 확인VM_SHARED | VM_WRITE → -EPERM, 읽기 전용 → VM_MAYWRITE 제거memfd_create("test", MFD_CLOEXEC | MFD_ALLOW_SEALING)
│
├─ sanitize_flags() ← MFD_* 플래그 유효성 검증
├─ alloc_name() ← "memfd:test" 이름 생성
└─ memfd_alloc_file()
├─ shmem_file_setup() ← tmpfs 익명 파일 생성 (기본)
│ 또는
├─ hugetlb_file_setup() ← HugeTLB 파일 생성 (MFD_HUGETLB)
├─ security_inode_init_security_anon()
└─ 파일 모드 설정 (FMODE_LSEEK 등)
fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE)
│
└─ memfd_add_seals()
├─ mapping_deny_writable() ← 쓰기 매핑 거부
└─ memfd_wait_for_pins()
├─ memfd_tag_pins() ← pinned folio 태깅
└─ 스캔 + 백오프 대기 (최대 ~150ms)
mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)
│
└─ memfd_check_seals_mmap()
└─ check_write_seal() ← write 봉인 시 -EPERM
| 구분 | tmpfs 백엔드 (기본) | hugetlbfs 백엔드 (MFD_HUGETLB) |
|---|---|---|
| **생성 함수** | `shmem_file_setup()` | `hugetlb_file_setup()` |
| **할당 시점** | demand paging (폴트 시) | 즉시 예약 (fallocate) |
| **커널 메모리 사용** | 실제 사용량만 | 사전 할당 (고정) |
| **성능** | 일반 메모리 수준 | 고성능 DMA 가능 |
| **Swappable** | 가능 | 불가능 |
| **봉인 구조체** | `shmem_inode_info.seals` | `hugetlbfs_inode_info.seals` |
| 봉인 플래그 | 효과 | SEAL_EXEC 시 자동 추가 |
|---|---|---|
| `F_SEAL_SEAL` | 추가 봉인 방지 | - |
| `F_SEAL_SHRINK` | 파일 축소 방지 | ✓ |
| `F_SEAL_GROW` | 파일 확장 방지 | ✓ |
| `F_SEAL_WRITE` | 현재 쓰기 방지 | ✓ |
| `F_SEAL_FUTURE_WRITE` | 향후 쓰기 방지 | ✓ |
| `F_SEAL_EXEC` | exec 비트 변경 방지 | - |
| noexec 범위 (sysctl) | 동작 |
|---|---|
| `MEMFD_NOEXEC_SCOPE_EXEC` (0) | `MFD_EXEC` 기본, noexec 없음 |
| `MEMFD_NOEXEC_SCOPE_NOEXEC_SEAL` (1) | `MFD_NOEXEC_SEAL` 자동 적용 |
| `MEMFD_NOEXEC_SCOPE_NOEXEC_ENFORCED` (2) | `MFD_NOEXEC_SEAL` 필수, 없으면 `-EACCES` |