๏ปฟ# 34. memfd โ ๋ฉ๋ชจ๋ฆฌ ๊ธฐ๋ฐ ํ์ผ ๋์คํฌ๋ฆฝํฐ
๊ฐ์ (Overview)
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))
"
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
shmem_inode_info โ tmpfs inode ๋ฉํ๋ฐ์ดํฐ (๋ด์ธ ์ ์ฅ)
// 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;
};
hugetlbfs_inode_info โ HugeTLB inode ๋ฉํ๋ฐ์ดํฐ
// include/linux/hugetlb.h:519-522
struct hugetlbfs_inode_info {
struct inode vfs_inode;
unsigned int seals; // memfd ๋ด์ธ ๋นํธ๋ง์คํฌ (shmem๊ณผ ๋์ผ ํ์)
};
F_SEAL_* ํ๋๊ทธ โ ํ์ผ ๋ด์ธ ์ ํ
// 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 ๋นํธ ๋ณ๊ฒฝ ๋ฐฉ์ง
MFD_* ํ๋๊ทธ โ memfd_create ์ต์
// 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 // ์คํ ๊ฐ๋ฅ
MEMFD_TAG_PINNED โ XArray ํ๊ทธ
// mm/memfd.c:29-30
#define MEMFD_TAG_PINNED PAGECACHE_TAG_TOWRITE // ์ถ๊ฐ ์ฐธ์กฐ๊ฐ ์๋ folio ํ๊ทธ
#define LAST_SCAN 4 // ์ต๋ ์ค์บ ํ์ (~150ms)
ํต์ฌ ํจ์
1. SYSCALL_DEFINE2(memfd_create) โ ์์คํ ํธ์ถ ์ง์ ์
// 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 ๋ฐฑ์๋ ์ ํ2. memfd_alloc_file โ ํ์ผ ์์ฑ
// 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 ๋นํธ ์ ๊ฑฐ3. memfd_add_seals โ ํ์ผ ๋ด์ธ ์ถ๊ฐ
// 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 ์ ์ฑ
์๋ ์ ์ฉ4. memfd_wait_for_pins โ pinned folio ๋๊ธฐ
// 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 ๋ฐํ
}
}
-EBUSY5. memfd_check_seals_mmap โ mmap ์ ๋ด์ธ ๊ฒ์ฆ
// 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` |