25. Memory Sealing (mseal)
๊ฐ์ (Overview)
mseal()์ Linux ์ปค๋์ ์์คํ
ํธ์ถ๋ก, ์ฌ์ฉ์ ๊ณต๊ฐ์์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์์ญ(VMA)์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋๋ก "๋ด์ธ(sealing)"ํ๋ ๋ณด์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ๋ฉ๋ชจ๋ฆฌ ๋ด์ธ์ RWX(Read/Write/Execute) ๋นํธ ์ธ์ ๋งคํ ์์ฒด๋ฅผ ๋ณดํธํ์ฌ, ์์๋ ํฌ์ธํฐ๊ฐ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์์คํ
์ ์
์ฉํ๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค. ์ด๋ XNU ์ปค๋์ VM_FLAGS_PERMANENT์ด๋ OpenBSD์ mimmutable๊ณผ ์ ์ฌํ ๊ธฐ๋ฅ์
๋๋ค.
Linux 7.0์์ mseal()์ 64๋นํธ ์ํคํ
์ฒ์์๋ง ์ง์๋๋ฉฐ(32๋นํธ๋ VM_SEALED๊ฐ VM_NONE์ผ๋ก ์ ์), CONFIG_64BIT์ด ํ์ฑํ๋ ์์คํ
์์๋ง ๋์ํฉ๋๋ค. ๋ด์ธ๋ ๋งคํ์ ํ๋ก์ธ์ค๊ฐ ์ข
๋ฃ๋๊ฑฐ๋ exec ์์คํ
ํธ์ถ์ด ๋ฐ์ํ ๋๊น์ง ํด์ ๋ ์ ์์ผ๋ฉฐ, ํ๋ฒ ๋ด์ธ๋ ๋ฉ๋ชจ๋ฆฌ๋ munseal ๊ธฐ๋ฅ์ด ์กด์ฌํ์ง ์์ต๋๋ค.
์์ค ํ์ผ ๊ฒฝ๋ก
mm/mseal.c # ํต์ฌ ๊ตฌํ (191์ค)
mm/vma.h # vma_is_sealed() ํฌํผ
mm/vma.c # munmap ๊ฒฝ๋ก์์ ๋ด์ธ ๊ฒ์ฌ
mm/mremap.c # mremap ์ฐจ๋จ ๊ฒ์ฌ
mm/mprotect.c # mprotect ์ฐจ๋จ ๊ฒ์ฌ
mm/madvise.c # madvise ์ฐจ๋จ ๊ฒ์ฌ
fs/binfmt_elf.c # ELF ๋ก๋์์ page 0 ์๋ ๋ด์ธ
include/linux/mm.h # VM_SEALED ํ๋๊ทธ ์ ์
init/Kconfig # CONFIG_MSEAL_SYSTEM_MAPPINGS
Documentation/userspace-api/mseal.rst # ๊ณต์ ๋ฌธ์
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# 1. mseal ์์คํ
ํธ์ถ ์ง์ ์ฌ๋ถ ํ์ธ
grep -r "CONFIG_64BIT" /boot/config-$(uname -r)
# 2. do_mseal ์ฌ๋ณผ ํ์ธ
grep -w "do_mseal" /proc/kallsyms 2>/dev/null || echo "do_mseal ์ฌ๋ณผ ๋ฏธ๋
ธ์ถ"
# 3. ๋ด์ธ ๊ด๋ จ ์ปค๋ ๋ชจ๋/์ค์ ํ์ธ
zgrep "MSEAL" /proc/config.gz 2>/dev/null || grep "MSEAL" /boot/config-$(uname -r) 2>/dev/null
# 4. ์์คํ
๋งคํ ๋ด์ธ ํ์ฑํ ํ์ธ
cat /proc/sys/vm/mmap_min_addr # ์ต์ ์ฃผ์ ํ์ธ
grep -n "vdso\|vvar" /proc/self/maps # ์์คํ
๋งคํ ํ์ธ
# 5. mseal ๊ด๋ จ ์ปค๋ ๋ก๊ทธ ํ์ธ
dmesg | grep -i "seal\|mseal" 2>/dev/null
# 6. seccomp์์ mseal ํธ์ถ ํ์ฉ ์ฌ๋ถ ํ์ธ
grep -i seccomp /proc/self/status
# 7. VMA ๊ฐ์ ์ ํ ํ์ธ (๋ด์ธ ์ VMA ๋ถํ ๊ฐ๋ฅ์ฑ)
cat /proc/sys/vm/max_map_count
# 8. ํน์ ํ๋ก์ธ์ค์ ๋ด์ธ๋ ๋งคํ ํ์ธ (VmFlags์ 'sl' ํ์)
grep -A 5 "VmFlags:.*sl" /proc/<pid>/smaps
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
mm_struct / vm_area_struct
mseal()์ ํ์ฌ ์์
์ ์ฃผ์ ๊ณต๊ฐ(struct mm_struct)๊ณผ ๊ทธ ์์ VMA(struct vm_area_struct)๋ฅผ ํจ๊ป ๋ค๋ฃน๋๋ค. mm_struct๋ ์ ๊ธ๊ณผ ๋ฒ์ ๊ฒ์์ ๊ธฐ์ค์ด ๋๊ณ , vm_area_struct๋ vm_start, vm_end, vm_flags๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ด์ธ ์ฌ๋ถ๋ฅผ ๋ฐ๊ฟ๋๋ค.
/* mm/mseal.c:55-65 */
static int mseal_apply(struct mm_struct *mm,
unsigned long start, unsigned long end)
{
struct vm_area_struct *vma, *prev;
VMA_ITERATOR(vmi, mm, start);
vma = vma_iter_load(&vmi);
prev = vma_prev(&vmi);
if (start > vma->vm_start)
prev = vma;
struct mm_struct: ํ์ฌ ํ๋ก์ธ์ค์ ์ฃผ์ ๊ณต๊ฐ ์ ์ฒด๋ฅผ ๋ํํฉ๋๋ค. mmap_write_lock_killable(mm)์ range_contains_unmapped(mm, ...)์ ๊ธฐ์ค์
๋๋ค.struct vm_area_struct: ๋ด์ธ ๋์ ๊ตฌ๊ฐ์
๋๋ค. vma_modify_flags()๊ฐ ์ด ๊ตฌ์กฐ์ฒด์ vm_flags๋ฅผ ๊ฐฑ์ ํฉ๋๋ค.VM_SEALED ํ๋๊ทธ
VM_SEALED๋ vm_area_struct.vm_flags์ ์ค์ ๋๋ ๋นํธ ํ๋๊ทธ๋ก, ํด๋น VMA๊ฐ ๋ด์ธ๋์์์ ๋ํ๋
๋๋ค.
/* include/linux/mm.h:500-506 */
#ifdef CONFIG_64BIT
#define VM_SEALED INIT_VM_FLAG(SEALED) /* 64๋นํธ์์๋ง ์ ํจ */
#else
#define VM_SEALED VM_NONE /* 32๋นํธ์์๋ ๋นํ์ฑ */
#endif
/* include/linux/mm.h:536-540 - ์์คํ
๋งคํ ๋ด์ธ */
#ifdef CONFIG_MSEAL_SYSTEM_MAPPINGS
#define VM_SEALED_SYSMAP VM_SEALED /* ์์คํ
๋งคํ ์๋ ๋ด์ธ */
#else
#define VM_SEALED_SYSMAP VM_NONE /* ์์คํ
๋งคํ ๋ด์ธ ๋นํ์ฑ */
#endif
/proc//smaps ์ VmFlags์๋ sl๋ก ํ์๋ฉ๋๋ค.vma_is_sealed() ํฌํผ ํจ์
VMA๊ฐ ๋ด์ธ๋์๋์ง ํ์ธํ๋ ์ธ๋ผ์ธ ํจ์์ ๋๋ค.
/* mm/vma.h:661-671 */
#ifdef CONFIG_64BIT
static inline bool vma_is_sealed(struct vm_area_struct *vma)
{
return (vma->vm_flags & VM_SEALED); /* VM_SEALED ๋นํธ ๊ฒ์ฌ */
}
#else
static inline bool vma_is_sealed(struct vm_area_struct *vma)
{
return false; /* 32๋นํธ์์๋ ํญ์ false */
}
#endif
range_contains_unmapped() - VMA ํ ๊ฒ์ฌ
mseal() ํธ์ถ ์ ์์/๋ ์ฃผ์ ์ฌ์ด์ ๋งคํ๋์ง ์์ ์์ญ(VMA hole)์ด ์๋์ง ํ์ธํฉ๋๋ค.
/* mm/mseal.c:38-53 */
static bool range_contains_unmapped(struct mm_struct *mm,
unsigned long start, unsigned long end)
{
struct vm_area_struct *vma;
unsigned long prev_end = start;
VMA_ITERATOR(vmi, current->mm, start);
for_each_vma_range(vmi, vma, end) {
if (vma->vm_start > prev_end)
return true; /* ์ด์ ๋๊ณผ ํ์ฌ ์์ ์ฌ์ด์ ํ ์กด์ฌ */
prev_end = vma->vm_end;
}
return prev_end < end; /* ๋ง์ง๋ง VMA๊ฐ end๋ณด๋ค ๋จผ์ ๋๋๋ ๊ฒฝ์ฐ */
}
mseal_apply() - ๋ด์ธ ์ ์ฉ
์ค์ ๋ก VMA์ VM_SEALED ํ๋๊ทธ๋ฅผ ์ค์ ํ๋ ํจ์์
๋๋ค. VMA ๋ถํ /๋ณํฉ์ด ํ์ํ ์ ์์ต๋๋ค.
/* mm/mseal.c:55-85 */
static int mseal_apply(struct mm_struct *mm,
unsigned long start, unsigned long end)
{
struct vm_area_struct *vma, *prev;
VMA_ITERATOR(vmi, mm, start);
vma = vma_iter_load(&vmi);
prev = vma_prev(&vmi);
if (start > vma->vm_start)
prev = vma;
for_each_vma_range(vmi, vma, end) {
const unsigned long curr_start = MAX(vma->vm_start, start);
const unsigned long curr_end = MIN(vma->vm_end, end);
if (!(vma->vm_flags & VM_SEALED)) {
vm_flags_t vm_flags = vma->vm_flags | VM_SEALED;
/* VMA ์์ (๋ถํ /๋ณํฉ ํ์ ์) */
vma = vma_modify_flags(&vmi, prev, vma, curr_start,
curr_end, &vm_flags);
if (IS_ERR(vma))
return PTR_ERR(vma);
vm_flags_set(vma, VM_SEALED);
}
prev = vma;
}
return 0;
}
ํต์ฌ ํจ์
1. do_mseal() - ๋ฉ์ธ ์ง์ ์
/* mm/mseal.c:139-185 */
int do_mseal(unsigned long start, size_t len_in, unsigned long flags)
mseal() ์์คํ
ํธ์ถ์ ๋ฉ์ธ ๋ก์ง. ์
๋ ฅ ๊ฒ์ฆ ํ ๋ด์ธ ์ ์ฉ - flags != 0 โ -EINVAL (์์ฝ๋ ํ๋๊ทธ)
- start๊ฐ ํ์ด์ง ์ ๋ ฌ๋์ง ์์ โ -EINVAL
- len_in์ด 0์ผ๋ก ๋ผ์ด๋ฉ๋จ โ -EINVAL
- end < start (์ค๋ฒํ๋ก์ฐ) โ -EINVAL
- end == start โ ์ฑ๊ณต (์๋ฌด ์์
์์)
- range_contains_unmapped()๊ฐ true โ -ENOMEM
- mseal_apply() ์คํจ โ ํด๋น ์๋ฌ ์ฝ๋ ๋ฐํ
2. SYSCALL_DEFINE3(mseal) - ์์คํ ํธ์ถ ์ ์
/* mm/mseal.c:187-191 */
SYSCALL_DEFINE3(mseal, unsigned long, start, size_t, len, unsigned long, flags)
{
return do_mseal(start, len, flags);
}
int mseal(void *addr, size_t len, unsigned long flags)3. can_madvise_modify() - madvise ํ์ฉ ๊ฒ์ฌ
/* mm/madvise.c:1297-1331 */
static bool can_madvise_modify(struct madvise_behavior *madv_behavior)
{
struct vm_area_struct *vma = madv_behavior->vma;
/* ๋ด์ธ๋์ง ์์ VMA๋ฉด ํ์ฉ */
if (!vma_is_sealed(vma))
return true;
/* ๋ด์ธ๋ VMA๋ผ๋ discard๊ฐ ์๋๋ฉด ํ์ฉ */
if (!is_discard(madv_behavior->behavior))
return true;
/* ์ต๋ช
read-only SEALED ๋งคํ์ ์ผ๋ถ discard๋ ํ์ฉ */
if (!vma_is_anonymous(vma))
return true;
/* ์ฐ๊ธฐ ๊ฐ๋ฅํ ๋งคํ์ด๋ฉด ํ์ฉ */
if ((vma->vm_flags & VM_WRITE) &&
arch_vma_access_permitted(vma, /* ์ฐ๊ธฐ= */ true,
/* ์คํ= */ false, /* ์ธ๋ถ= */ false))
return true;
/* ๊ทธ ์ธ์๋ ํ์ฉํ์ง ์์ */
return false;
}
madvise() ํธ์ถ์ด ํ์ฉ๋๋์ง ํ๋จ- VMA๊ฐ ๋ด์ธ๋์ง ์์ โ ํ์ฉ
- discard ์ฐ์ฐ์ด ์๋ โ ํ์ฉ
- ํ์ผ ๊ธฐ๋ฐ ๋งคํ (shared/private) โ ํ์ฉ
- ์ฐ๊ธฐ ๊ฐ๋ฅ ๋งคํ โ ํ์ฉ
- ๊ทธ ์ธ (์ต๋ช ์ฝ๊ธฐ ์ ์ฉ ๋ด์ธ ๋งคํ) โ ์ฐจ๋จ
4. is_discard() - discard ๊ณ์ด ํ๋ณ
/* mm/madvise.c:1273-1284 */
static bool is_discard(int behavior)
{
switch (behavior) {
case MADV_FREE:
case MADV_DONTNEED:
case MADV_DONTNEED_LOCKED:
case MADV_REMOVE:
case MADV_DONTFORK:
case MADV_WIPEONFORK:
case MADV_GUARD_INSTALL:
return true;
}
return false;
}
madvise()๊ฐ ๋ฐ์ดํฐ ํ๊ธฐ ๊ณ์ด์ธ์ง ๋จผ์ ๊ฐ๋ ค๋ธ ๋ค, ๋ด์ธ๋ ์ต๋ช
์ฝ๊ธฐ ์ ์ฉ VMA์์๋ง ์ ํ์ ๊ฑธ์ด๋ก๋๋ค.5. vma_is_sealed() - ๋ด์ธ ์ํ ํ์ธ
/* mm/vma.h:662-664 */
static inline bool vma_is_sealed(struct vm_area_struct *vma)
{
return (vma->vm_flags & VM_SEALED);
}
vm_flags์์ VM_SEALED ๋นํธ ๊ฒ์ฌmremap, mprotect, munmap, madvise ๋ฑ์์ ํธ์ถ6. range_contains_unmapped() - VMA ํ ๊ฒ์ฆ
/* mm/mseal.c:38-53 */
static bool range_contains_unmapped(struct mm_struct *mm,
unsigned long start, unsigned long end)
true, ์์ผ๋ฉด falseํธ์ถ ํ๋ฆ
์ฌ์ฉ์ ๊ณต๊ฐ
โ
โผ
mseal(addr, len, flags) โ ์์คํ
ํธ์ถ
โ
โผ
SYSCALL_DEFINE3(mseal) โ mm/mseal.c:187
โ
โผ
do_mseal(start, len, flags) โ mm/mseal.c:139
โ
โโโบ flags ๊ฒ์ฆ โ flags != 0 โ -EINVAL
โโโบ untagged_addr(start) โ ํ๊ทธ ์ ๊ฑฐ (MTE ๋ฑ)
โโโบ PAGE_ALIGNED(start) ๊ฒ์ฆ โ ํ์ด์ง ์ ๋ ฌ ํ์ธ
โโโบ len = PAGE_ALIGN(len_in) โ ๊ธธ์ด ์ ๋ ฌ
โโโบ end = start + len โ ์ค๋ฒํ๋ก์ฐ ๊ฒ์ฌ
โ
โโโบ mmap_write_lock_killable(mm) โ ์ฐ๊ธฐ ์ ๊ธ ํ๋
โ
โโโบ range_contains_unmapped(mm, start, end) โ VMA ํ ๊ฒ์ฌ
โ โ
โ โโโบ for_each_vma_range() โ ํ ๋ฐ๊ฒฌ ์ -ENOMEM
โ
โโโบ mseal_apply(mm, start, end) โ VM_SEALED ํ๋๊ทธ ์ค์
โ โ
โ โโโบ for_each_vma_range()
โ โโโบ vma_modify_flags() โ VMA ๋ถํ /๋ณํฉ
โ โโโบ vm_flags_set(vma, VM_SEALED)
โ
โโโบ mmap_write_unlock(mm) โ ์ฐ๊ธฐ ์ ๊ธ ํด์
๋ด์ธ ํ ์ฐจ๋จ๋๋ ์์คํ ํธ์ถ ํ๋ฆ
๋ด์ธ๋ VMA์์ ํธ์ถ ์
โ
โโโบ munmap() โ mm/vma.c:1403,1423 โ -EPERM
โโโบ mremap() โ mm/mremap.c:1666 โ -EPERM
โโโบ mprotect() โ mm/mprotect.c:706 โ -EPERM
โโโบ madvise() โ mm/madvise.c:1302 โ ํ์ฉ/์ฐจ๋จ ๋ถ๊ธฐ
์กฐ๊ฑด๋ณ ๋น๊ต
๋ด์ธ ํ ์์คํ ํธ์ถ ํ์ฉ ์ฌ๋ถ
| ์์คํ ํธ์ถ | ํ์ฉ ์ฌ๋ถ | ๊ฒ์ฌ ์์น | ๋น๊ณ |
|---|---|---|---|
| `munmap()` | ์ฐจ๋จ (-EPERM) | mm/vma.c:1403 | ๋ด์ธ๋ VMA ๋ถํ ์๋ ์ |
| `mremap()` | ์ฐจ๋จ (-EPERM) | mm/mremap.c:1666 | ์ด๋/ํ์ฅ/์ถ์ ๋ชจ๋ ์ฐจ๋จ |
| `mprotect()` | ์ฐจ๋จ (-EPERM) | mm/mprotect.c:706 | RWX ๋ณดํธ ๋นํธ ๋ณ๊ฒฝ ์ฐจ๋จ |
| `pkey_mprotect()` | ์ฐจ๋จ (-EPERM) | mm/mprotect.c:706 | pkey ํฌํจ mprotect |
| `madvise(discard ๊ณ์ด)` | ์กฐ๊ฑด๋ถ ์ฐจ๋จ | mm/madvise.c:1274,1302 | `MADV_FREE`, `MADV_DONTNEED`, `MADV_DONTNEED_LOCKED`, `MADV_REMOVE`, `MADV_DONTFORK`, `MADV_WIPEONFORK`, `MADV_GUARD_INSTALL` |
| `madvise(๋น-discard)` | ํ์ฉ | mm/madvise.c:1306 | `is_discard()`๊ฐ false์ด๋ฉด ํ์ฉ |
| `mmap(MAP_FIXED)` | ์ฐจ๋จ (๊ฐ์ ) | mm/vma.c:1403 | ๊ธฐ์กด ๋งคํ ๊ต์ฒด ๋ถ๊ฐ |
mseal() ํธ์ถ ์กฐ๊ฑด๋ณ ๊ฒฐ๊ณผ
| ์กฐ๊ฑด | ๊ฒฐ๊ณผ | ๋น๊ณ |
|---|---|---|
| `flags != 0` | -EINVAL | ์์ฝ๋ ํ๋๊ทธ |
| ์์ ์ฃผ์ ๋ฏธ์ ๋ ฌ | -EINVAL | ํ์ด์ง ์ ๋ ฌ ํ์ |
| ๋ฒ์ ์ค๋ฒํ๋ก์ฐ | -EINVAL | start + len > ULONG_MAX |
| ๋น ๋ฒ์ (len=0) | ์ฑ๊ณต (0) | ์๋ฌด ์์ ์์ |
| VMA ํ ์กด์ฌ | -ENOMEM | ์์/๋ ์ฌ์ด ๋งคํ ์์ |
| 32๋นํธ ์ํคํ ์ฒ | -EPERM | mseal ๋ฏธ์ง์ |
| ์ด๋ฏธ ๋ด์ธ๋ VMA | ์ฑ๊ณต (0) | ๋ฉฑ๋ฑ์ฑ (idempotent) |
| ์ ์์ ์ธ ๋ด์ธ | ์ฑ๊ณต (0) | VM_SEALED ํ๋๊ทธ ์ค์ |
์์คํ ๋งคํ ๋ด์ธ (CONFIG_MSEAL_SYSTEM_MAPPINGS)
| ์ค์ | ๋์ | ์ง์ ์ํคํ ์ฒ |
|---|---|---|
| `CONFIG_MSEAL_SYSTEM_MAPPINGS=y` | vdso, vvar ๋ฑ ์์คํ ๋งคํ ์๋ ๋ด์ธ | x86-64, arm64, loongarch, s390 |
| `CONFIG_MSEAL_SYSTEM_MAPPINGS=n` | ์์คํ ๋งคํ ๋ด์ธ ๋นํ์ฑ | ๋ชจ๋ ์ํคํ ์ฒ |
| `CONFIG_ARCH_SUPPORTS_MSEAL_SYSTEM_MAPPINGS` | ์ํคํ ์ฒ ์์ค ์ง์ | x86-64, arm64, loongarch, s390 |
๋ด์ธ ์ฌ์ฉ ์ฌ๋ก
1. ELF ๋ก๋ ์๋ ๋ด์ธ
/* fs/binfmt_elf.c:1358 - SVr4 ํธํ page 0 ์๋ ๋ด์ธ */
if (current->personality & MMAP_PAGE_ZERO) {
error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
retval = do_mseal(0, PAGE_SIZE, 0); /* page 0 ๋ด์ธ */
}
2. ๋ณด์ ์ ์ฉ ์๋๋ฆฌ์ค
3. ์ฌ์ฉ ๋ถ๊ฐ ์๋๋ฆฌ์ค
๋ด์ธ ๋งคํ์์์ ์ฐ๊ธฐ ๊ฐ๋ฅ ๊ฒฝ๋ก
๋ด์ธ๋ ์ฝ๊ธฐ ์ ์ฉ ๋งคํ์๋ ์ฐ๊ธฐ๊ฐ ๊ฐ๋ฅํ ๊ฒฝ๋ก๊ฐ ์์ต๋๋ค (๋ณด์ ๊ณ ๋ ค์ฌํญ):
| ๊ฒฝ๋ก | ๋ฉ์ปค๋์ฆ | ์ฐจ๋จ ๋ฐฉ๋ฒ |
|---|---|---|
| `/proc/self/mem` | FOLL_FORCE๋ก ์ฐ๊ธฐ | SELinux/SMACK |
| `ptrace` | PTRACE_POKETEXT | seccomp |
| `userfaultfd` | ํ์ด์ง ํดํธ ์ฒ๋ฆฌ | seccomp |