๏ปฟ# mprotect ๐Ÿ›ก๏ธ

๊ด€๋ จ ์†Œ์Šค: mm/mprotect.c, include/linux/mman.h, include/linux/mm.h

๊ฐœ์š” (Overview)

mprotect๋Š” ํ”„๋กœ์„ธ์Šค์˜ ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ(VMA)์— ๋Œ€ํ•œ ๋ณดํ˜ธ ์†์„ฑ(์ฝ๊ธฐ/์“ฐ๊ธฐ/์‹คํ–‰)์„ ๋Ÿฐํƒ€์ž„์— ๋ณ€๊ฒฝํ•˜๋Š” ์‹œ์Šคํ…œ ํ˜ธ์ถœ์ด๋‹ค. Linux 7.0์—์„œ mprotect๋Š” ๋‹จ์ˆœํ•œ PTE ๋ณดํ˜ธ ๋ณ€๊ฒฝ์„ ๋„˜์–ด NUMA ๋ฐธ๋Ÿฐ์‹ฑ, userfaultfd-wp(write-protect), softdirty ํŠธ๋ž˜ํ‚น, memory sealing ๋“ฑ ๋‹ค์–‘ํ•œ ์„œ๋ธŒ์‹œ์Šคํ…œ๊ณผ ๊นŠ์ด ํ†ตํ•ฉ๋˜์–ด ์žˆ๋‹ค. ํ•ต์‹ฌ ๊ฒฝ๋กœ๋Š” do_mprotect_pkey() โ†’ mprotect_fixup() โ†’ change_protection() โ†’ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๊ณ„์ธต(PGDโ†’P4Dโ†’PUDโ†’PMDโ†’PTE) ์ˆœํšŒ์ด๋‹ค.

์†Œ์Šค ํŒŒ์ผ์€ mm/mprotect.c(1007์ค„) ํ•˜๋‚˜๋กœ, ์‹œ์Šคํ…œ ํ˜ธ์ถœ ์ •์˜(SYSCALL_DEFINE3)๋ถ€ํ„ฐ PTE ๋ฐฐ์น˜(batching) ์ตœ์ ํ™”, HugePMD/HugePUD ๋ถ„ํ• , PROT_NONE ๊ถŒํ•œ ๊ฒ€์ฆ๊นŒ์ง€ ๋ชจ๋‘ ํฌํ•จํ•œ๋‹ค. pkey_mprotect/pkey_alloc/pkey_free ์‹œ์Šคํ…œ ํ˜ธ์ถœ๋„ ํ•จ๊ป˜ ์ •์˜๋˜์–ด ์žˆ์–ด ๋ฉ”๋ชจ๋ฆฌ ๋ณดํ˜ธ ํ‚ค(per-process protection key) ์ง€์›๋„ ๋‹ด๋‹นํ•œ๋‹ค.

์ฑ…์žฅ์— ๋ถ™์€ ์ถœ์ž… ๊ทœ์น™์„ ๋ฐ”๊พธ๋Š” ์ผ๋กœ ๋ณด๋ฉด ์ดํ•ด๊ฐ€ ์‰ฝ๋‹ค. ์ฑ…์žฅ(VMA)์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ , "์ฝ๊ธฐ๋งŒ", "์“ฐ๊ธฐ ํ—ˆ์šฉ", "์‹คํ–‰ ํ—ˆ์šฉ" ๊ฐ™์€ ํ‘œ์ง€ํŒ๋งŒ ๋ฐ”๊พธ๋Š” ์ž‘์—…์ด mprotect์ด๊ณ , ์‹ค์ œ ์ฑ…(PTE)์€ ํ•„์š”ํ•  ๋•Œ๋งŒ ๋‹ค์‹œ ์ •๋ฆฌ๋œ๋‹ค.

# ์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ
mm/mprotect.c                  โ† ๋ฉ”์ธ ์†Œ์Šค (1007์ค„)
include/linux/mman.h           โ† calc_vm_prot_bits, map_deny_write_exec
include/linux/mm.h             โ† VM_* ํ”Œ๋ž˜๊ทธ, MM_CP_* ํ”Œ๋ž˜๊ทธ, can_change_pte_writable
include/linux/mm_types.h       โ† struct vm_area_struct
mm/internal.h                  โ† folio_can_map_prot_numa
include/linux/pgtable.h        โ† modify_prot_start_ptes, modify_prot_commit_ptes

mprotect ํ˜ธ์ถœ ํ๋ฆ„
mprotect ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

๋น ๋ฅธ ์ ๊ฒ€ ๋ช…๋ น

# 1. ํŠน์ • ํ”„๋กœ์„ธ์Šค์˜ VMA ๊ถŒํ•œ๊ณผ pkey ํ™•์ธ
grep -nE '^VmFlags:|^ProtectionKey:' /proc/<pid>/smaps | head -40

# 2. mprotect ์‹œ์Šคํ…œ ํ˜ธ์ถœ ์นด์šดํŠธ ํ™•์ธ
perf stat -e 'syscalls:sys_enter_mprotect' -p <pid> sleep 5

# 3. mprotect ๊ด€๋ จ strace ์ถ”์ 
strace -e mprotect -p <pid>

# 4. ํ˜„์žฌ ๋ฉ”๋ชจ๋ฆฌ ๋ณดํ˜ธ ์ƒํƒœ (rwxp ๋“ฑ)
cat /proc/<pid>/maps | head -20

# 5. pkey๊ฐ€ ๋ถ™์€ ๋งคํ•‘ ํ™•์ธ
grep -E '^ProtectionKey:' /proc/<pid>/smaps | sort | uniq -c

# 6. ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์‚ฌ์šฉ๋Ÿ‰ ์ถ”์ 
cat /proc/<pid>/status | grep VmPTE

# 7. mprotect ํ˜ธ์ถœ ์‹œ ํŠธ๋ ˆ์ด์‹ฑ (ftrace)
echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_mprotect/enable

# 8. pkey_mprotect ํ˜ธ์ถœ ๋นˆ๋„ ํ™•์ธ
perf stat -e 'syscalls:sys_enter_pkey_mprotect' -p <pid> sleep 5

# 9. ๋ฉ”๋ชจ๋ฆฌ ๋ณดํ˜ธ ๊ด€๋ จ ์ปค๋„ ๋กœ๊ทธ
dmesg | grep -i "mprotect\|pkey\|protection"

# 10. x86์—์„œ MDWE(Memory Deny Write Execute) ์ƒํƒœ ํ™•์ธ
cat /proc/<pid>/status | grep -i mdwe

ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ

struct vm_area_struct (์„ ํƒ ํ•„๋“œ)

// include/linux/mm_types.h:913
struct vm_area_struct {
unsigned long vm_start;          // VMA ์‹œ์ž‘ ์ฃผ์†Œ
unsigned long vm_end;            // VMA ์ข…๋ฃŒ ์ฃผ์†Œ (ํฌํ•จ ์•ˆ ํ•จ)
struct mm_struct *vm_mm;         // ์†Œ์† mm_struct
pgprot_t vm_page_prot;           // ํ•˜๋“œ์›จ์–ด PTE์— ์‚ฌ์šฉ๋  ๋ณดํ˜ธ ์†์„ฑ
union {
const vm_flags_t vm_flags;   // VM_READ, VM_WRITE, VM_EXEC ๋“ฑ
vma_flags_t flags;
};
const struct vm_operations_struct *vm_ops; // mprotect ์ฝœ๋ฐฑ (ํŒŒ์ผ ์‹œ์Šคํ…œ๋ณ„)
struct anon_vma *anon_vma;       // ์ต๋ช… ๋งคํ•‘์šฉ anon_vma
unsigned long vm_pgoff;          // ์˜คํ”„์…‹ (PAGE_SIZE ๋‹จ์œ„)
struct file *vm_file;            // ๋งคํ•‘๋œ ํŒŒ์ผ (์—†์œผ๋ฉด NULL)
struct mempolicy *vm_policy;     // NUMA ์ •์ฑ… (CONFIG_NUMA)
};

* ํŒŒ์ผ: include/linux/mm_types.h:913-986 (ํ•ต์‹ฌ ํ•„๋“œ)

VM_* ํ”Œ๋ž˜๊ทธ โ€” mprotect ๊ด€๋ จ ํ•ต์‹ฌ

// include/linux/mm.h:402-545 (์„ ํƒ)
#define VM_READ       INIT_VM_FLAG(READ)      // ์ฝ๊ธฐ ๊ฐ€๋Šฅ
#define VM_WRITE      INIT_VM_FLAG(WRITE)     // ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
#define VM_EXEC       INIT_VM_FLAG(EXEC)      // ์‹คํ–‰ ๊ฐ€๋Šฅ
#define VM_SHARED     INIT_VM_FLAG(SHARED)    // ๊ณต์œ  ๋งคํ•‘
#define VM_MAYREAD    INIT_VM_FLAG(MAYREAD)   // ์ฝ๊ธฐ ํ—ˆ์šฉ ๊ฐ€๋Šฅ
#define VM_MAYWRITE   INIT_VM_FLAG(MAYWRITE)  // ์“ฐ๊ธฐ ํ—ˆ์šฉ ๊ฐ€๋Šฅ
#define VM_MAYEXEC    INIT_VM_FLAG(MAYEXEC)   // ์‹คํ–‰ ํ—ˆ์šฉ ๊ฐ€๋Šฅ
#define VM_LOCKED     INIT_VM_FLAG(LOCKED)    // ๊ณ ์ • (์Šค์™‘ ์•„์›ƒ ์•ˆ ๋จ)
#define VM_SEALED     INIT_VM_FLAG(SEALED)    // ๋ด‰์ธ (mprotect ๊ฑฐ๋ถ€)
#define VM_PFNMAP     INIT_VM_FLAG(PFNMAP)    // ์ปค๋„ ์ง์ ‘ ๋งคํ•‘
#define VM_ACCOUNT    INIT_VM_FLAG(ACCOUNT)   // ์˜ค๋ฒ„์ปค๋ฐ‹ ๊ณ„์ •

* ํŒŒ์ผ: include/linux/mm.h:402-545

* mprotect์—์„œ VM_ACCESS_FLAGS(VM_READ|VM_WRITE|VM_EXEC)๊ฐ€ VMA์— ์„ค์ •๋จ

* VM_SEALED๊ฐ€ ์„ค์ •๋œ VMA๋Š” mprotect๊ฐ€ -EPERM์œผ๋กœ ๊ฑฐ๋ถ€ (mseal ์—ฐ๋™)

MM_CP_* ํ”Œ๋ž˜๊ทธ โ€” change_protection ์ œ์–ด

// include/linux/mm.h:3031-3038
#define MM_CP_TRY_CHANGE_WRITABLE  (1UL << 0)  // ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ PTE๋กœ ๋ณ€๊ฒฝ ์‹œ๋„
#define MM_CP_PROT_NUMA            (1UL << 1)  // NUMA ๋ฐธ๋Ÿฐ์‹ฑ์šฉ PROT_NONE
#define MM_CP_UFFD_WP              (1UL << 2)  // userfaultfd ์“ฐ๊ธฐ ๋ณดํ˜ธ
#define MM_CP_UFFD_WP_RESOLVE      (1UL << 3)  // userfaultfd ์“ฐ๊ธฐ ๋ณดํ˜ธ ํ•ด์ œ
#define MM_CP_UFFD_WP_ALL          (MM_CP_UFFD_WP | MM_CP_UFFD_WP_RESOLVE)

* ํŒŒ์ผ: include/linux/mm.h:3031-3038

* MM_CP_TRY_CHANGE_WRITABLE: vma_wants_manual_pte_write_upgrade()๊ฐ€ true์ผ ๋•Œ ์„ค์ •

* MM_CP_PROT_NUMA: NUMA ๋ฐธ๋Ÿฐ์‹ฑ์ด PROT_NONE์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•  ๋•Œ ์‚ฌ์šฉ

can_change_pte_writable โ€” PTE ์“ฐ๊ธฐ ๋ณ€๊ฒฝ ์กฐ๊ฑด

// mm/mprotect.c:97-104
bool can_change_pte_writable(struct vm_area_struct *vma, unsigned long addr,
pte_t pte)
{
if (!(vma->vm_flags & VM_SHARED))
return can_change_private_pte_writable(vma, addr, pte);  // ๋น„๊ณต์œ : anon exclusive๋งŒ
return can_change_shared_pte_writable(vma, pte);             // ๊ณต์œ : dirty๋ฉด ํ—ˆ์šฉ
}

* ํŒŒ์ผ: mm/mprotect.c:97-104

* MAP_PRIVATE: PageAnon(page) && PageAnonExclusive(page)์ธ ๊ฒฝ์šฐ๋งŒ ์“ฐ๊ธฐ ๊ฐ€๋Šฅ

* MAP_SHARED: pte_dirty(pte)์ด๋ฉด writenotify ๋ถˆํ•„์š”ํ•˜๋ฏ€๋กœ ์“ฐ๊ธฐ ๊ฐ€๋Šฅ


ํ•ต์‹ฌ ํ•จ์ˆ˜

1. do_mprotect_pkey() โ€” ์‹œ์Šคํ…œ ํ˜ธ์ถœ ์ง„์ž…์ 

// mm/mprotect.c:801-946
static int do_mprotect_pkey(unsigned long start, size_t len,
unsigned long prot, int pkey)

* ์—ญํ• : mprotect()/pkey_mprotect() ์‹œ์Šคํ…œ ํ˜ธ์ถœ์˜ ๊ณตํ†ต ์ฒ˜๋ฆฌ ํ•จ์ˆ˜

* ๋ถ„๊ธฐ ๋กœ์ง:

1. PROT_GROWSDOWN|PROT_GROWSUP ๋™์‹œ ์„ค์ • ์‹œ EINVAL

2. mmap_write_lock_killable()๋กœ mmap_lock ํš๋“

3. pkey๊ฐ€ -1์ด ์•„๋‹ˆ๋ฉด mm_pkey_is_allocated()๋กœ ํ• ๋‹น ๊ฒ€์ฆ

4. vma_find()๋กœ ๋Œ€์ƒ VMA ํƒ์ƒ‰, ์—†์œผ๋ฉด ENOMEM

5. PROT_GROWSDOWN/PROT_GROWSUP ์ฒ˜๋ฆฌ๋กœ start/end ๋ฒ”์œ„ ํ™•์žฅ

6. for_each_vma_range()๋กœ ๊ฐ VMA์— ๋Œ€ํ•ด:

- rier(READ_IMPLIES_EXEC) ์ฒ˜๋ฆฌ

- calc_vm_prot_bits()๋กœ newflags ๊ณ„์‚ฐ

- VM_ACCESS_FLAGS ๊ฒ€์ฆ โ†’ ๊ถŒํ•œ ์—†๋Š” ํ”Œ๋ž˜๊ทธ ์š”์ฒญ ์‹œ EACCES

- map_deny_write_exec()๋กœ MDWE ์œ„๋ฐ˜ ๊ฒ€์‚ฌ

- security_file_mprotect()๋กœ LSM ๊ฒ€์ฆ

- vma->vm_ops->mprotect() ์ฝœ๋ฐฑ ํ˜ธ์ถœ (ํŒŒ์ผ ์‹œ์Šคํ…œ๋ณ„)

- mprotect_fixup()๋กœ ์‹ค์ œ VMA ์ˆ˜์ •

7. tlb_finish_mmu()๋กœ TLB ํ”Œ๋Ÿฌ์‹œ

2. mprotect_fixup() โ€” VMA ๋ณดํ˜ธ ์ˆ˜์ •

// mm/mprotect.c:695-796
int mprotect_fixup(struct vma_iterator *vmi, struct mmu_gather *tlb,
struct vm_area_struct *vma, struct vm_area_struct **pprev,
unsigned long start, unsigned long end, vm_flags_t newflags)

* ์—ญํ• : ๋‹จ์ผ VMA์˜ ๋ณดํ˜ธ ์†์„ฑ์„ ์‹ค์ œ๋กœ ๋ณ€๊ฒฝ

* ๋ถ„๊ธฐ ๋กœ์ง:

1. vma_is_sealed(vma) โ†’ EPERM (๋ด‰์ธ๋œ VMA ๊ฑฐ๋ถ€)

2. newflags == oldflags โ†’ ์ฆ‰์‹œ ๋ฐ˜ํ™˜

3. VM_PFNMAP|VM_MIXEDMAP์ด๊ณ  VM_ACCESS_FLAGS ์ œ๊ฑฐ ์‹œ walk_page_range()๋กœ PROT_NONE ์‚ฌ์ „ ๊ฒ€์ฆ

4. VM_WRITE ์ถ”๊ฐ€ ์‹œ ์˜ค๋ฒ„์ปค๋ฐ‹ ๊ฒ€์ฆ (may_expand_vm, security_vm_enough_memory_mm)

5. VM_ACCOUNT ํ•ด์ œ ์‹œ vm_unacct_memory()

6. vma_modify_flags()๋กœ VMA ๋ณ‘ํ•ฉ/๋ถ„ํ•  ์ฒ˜๋ฆฌ

7. vma_start_write() โ†’ vm_flags_reset_once() โ†’ vma_set_page_prot()

8. change_protection()๋กœ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์—…๋ฐ์ดํŠธ

9. VM_LOCKED โ†’ writable ์ „ํ™˜ ์‹œ populate_vma_page_range()๋กœ COW ํŠธ๋ฆฌ๊ฑฐ

3. change_protection() โ€” ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋ณดํ˜ธ ๋ณ€๊ฒฝ

// mm/mprotect.c:633-662
long change_protection(struct mmu_gather *tlb,
struct vm_area_struct *vma, unsigned long start,
unsigned long end, unsigned long cp_flags)

* ์—ญํ• : ์ง€์ •๋œ ์ฃผ์†Œ ๋ฒ”์œ„์˜ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋ณดํ˜ธ ์†์„ฑ์„ ๋ณ€๊ฒฝ

* ๋ถ„๊ธฐ ๋กœ์ง:

1. NUMA ๋ฐธ๋Ÿฐ์‹ฑ์ด๋ฉด newprot = PAGE_NONE (PROT_NONE์œผ๋กœ ๋น„ํ™œ์„ฑํ™”)

2. Hugetlb ํŽ˜์ด์ง€๋ฉด hugetlb_change_protection() ๋ถ„๊ธฐ

3. ์ผ๋ฐ˜ ํŽ˜์ด์ง€๋ฉด change_protection_range() ํ˜ธ์ถœ

4. change_protection_range() โ†’ change_pmd_range() โ†’ change_pte_range()

ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” 5๋‹จ๊ณ„(PGDโ†’P4Dโ†’PUDโ†’PMDโ†’PTE)๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๋ณดํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์žฌ๊ท€ ๊ตฌ์กฐ์ด๋‹ค.

change_protection_range()     โ† PGD ๋ ˆ๋ฒจ ์ˆœํšŒ
โ””โ”€ change_p4d_range()       โ† P4D ๋ ˆ๋ฒจ ์ˆœํšŒ
โ””โ”€ change_pud_range()  โ† PUD ๋ ˆ๋ฒจ ์ˆœํšŒ (huge pud ์ฒ˜๋ฆฌ)
โ””โ”€ change_pmd_range() โ† PMD ๋ ˆ๋ฒจ ์ˆœํšŒ (huge pmd ์ฒ˜๋ฆฌ)
โ””โ”€ change_pte_range() โ† ์‹ค์ œ PTE ์ˆ˜์ •

5. change_pte_range() โ€” PTE ๋ ˆ๋ฒจ ์ฒ˜๋ฆฌ

// mm/mprotect.c:214-386
static long change_pte_range(struct mmu_gather *tlb,
struct vm_area_struct *vma, pmd_t *pmd, unsigned long addr,
unsigned long end, pgprot_t newprot, unsigned long cp_flags)

* ์—ญํ• : PTE ํ•˜๋‚˜(๋˜๋Š” ๋ฐฐ์น˜)์˜ ๋ณดํ˜ธ ์†์„ฑ์„ ๋ณ€๊ฒฝ

* 3๊ฐ€์ง€ PTE ์ƒํƒœ ์ฒ˜๋ฆฌ:

1. pte_present(oldpte): ์กด์žฌํ•˜๋Š” ํŽ˜์ด์ง€ โ†’ pte_modify()๋กœ ๋ณดํ˜ธ ๋ณ€๊ฒฝ, uffd_wp/uffd_wp_resolve ์ฒ˜๋ฆฌ

2. pte_none(oldpte): ๋นˆ PTE โ†’ uffd_wp์ผ ๋•Œ PTE_MARKER_UFFD_WP ๋งˆ์ปค ์„ค์น˜

3. ๊ทธ ์™ธ (์Šค์™‘/๋งˆ์ด๊ทธ๋ ˆ์ด์…˜/๋””๋ฐ”์ด์Šค): softleaf_t๋กœ ํƒ€์ž… ๋ถ„๋ฅ˜ ํ›„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์—”ํŠธ๋ฆฌ, ๋””๋ฐ”์ด์Šค ํ”„๋ผ์ด๋น— ์—”ํŠธ๋ฆฌ, ๋งˆ์ปค ์—”ํŠธ๋ฆฌ ๊ฐ๊ฐ ์ฒ˜๋ฆฌ

* ๋ฐฐ์น˜ ์ตœ์ ํ™”: mprotect_folio_pte_batch()๋กœ large folio์˜ ์—ฐ์† PTE๋ฅผ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌ

6. pkey_alloc / pkey_free / pkey_mprotect โ€” ๋ฉ”๋ชจ๋ฆฌ ๋ณดํ˜ธ ํ‚ค

// mm/mprotect.c:956-1005
SYSCALL_DEFINE4(pkey_mprotect, unsigned long, start, size_t, len,
unsigned long, prot, int, pkey)
SYSCALL_DEFINE2(pkey_alloc, unsigned long, flags, unsigned long, init_val)
SYSCALL_DEFINE1(pkey_free, int, pkey)

* pkey_mprotect: pkey๋ฅผ ์ง€์ •ํ•˜์—ฌ mprotect ์ ์šฉ (x86 Memory Protection Keys)

* pkey_alloc: ์ƒˆ ๋ณดํ˜ธ ํ‚ค ํ• ๋‹น ํ›„ arch_set_user_pkey_access()๋กœ ๊ถŒํ•œ ์„ค์ •

* pkey_free: ๋ณดํ˜ธ ํ‚ค ํ•ด์ œ


ํ˜ธ์ถœ ํ๋ฆ„

sys_mprotect(start, len, prot)
โ””โ”€ do_mprotect_pkey(start, len, prot, pkey=-1)
โ”œโ”€ mmap_write_lock_killable()          โ† mmap_lock ์“ฐ๊ธฐ ์ž ๊ธˆ
โ”œโ”€ vma_find()                          โ† ๋Œ€์ƒ VMA ํƒ์ƒ‰
โ”œโ”€ [for_each_vma_range] ๊ฐ VMA ๋ฐ˜๋ณต:
โ”‚    โ”œโ”€ calc_vm_prot_bits()            โ† PROT_* โ†’ VM_* ๋ณ€ํ™˜
โ”‚    โ”œโ”€ map_deny_write_exec()          โ† MDWE ๊ฒ€์ฆ
โ”‚    โ”œโ”€ security_file_mprotect()       โ† LSM ๋ณด์•ˆ ๊ฒ€์ฆ
โ”‚    โ”œโ”€ vma->vm_ops->mprotect()        โ† ํŒŒ์ผ ์‹œ์Šคํ…œ ์ฝœ๋ฐฑ
โ”‚    โ””โ”€ mprotect_fixup()               โ† VMA ์ˆ˜์ •
โ”‚         โ”œโ”€ vma_is_sealed()           โ† ๋ด‰์ธ ๊ฒ€์‚ฌ
โ”‚         โ”œโ”€ vma_modify_flags()        โ† VMA ๋ณ‘ํ•ฉ/๋ถ„ํ• 
โ”‚         โ”œโ”€ vm_flags_reset_once()     โ† ํ”Œ๋ž˜๊ทธ ์—…๋ฐ์ดํŠธ
โ”‚         โ”œโ”€ vma_set_page_prot()       โ† vm_page_prot ๊ฐฑ์‹ 
โ”‚         โ””โ”€ change_protection()       โ† PTE ๋ณดํ˜ธ ๋ณ€๊ฒฝ
โ”‚              โ”œโ”€ [Hugetlb] hugetlb_change_protection()
โ”‚              โ””โ”€ [์ผ๋ฐ˜] change_protection_range()
โ”‚                   โ””โ”€ change_p4d_range() โ†’ change_pud_range()
โ”‚                        โ†’ change_pmd_range()
โ”‚                             โ””โ”€ change_pte_range()
โ”‚                                  โ”œโ”€ pte_present: pte_modify + TLB flush
โ”‚                                  โ”œโ”€ pte_none: uffd-wp ๋งˆ์ปค ์„ค์น˜
โ”‚                                  โ””โ”€ swap/dev: softleaf ์ฒ˜๋ฆฌ
โ””โ”€ tlb_finish_mmu()                    โ† TLB ํ”Œ๋Ÿฌ์‹œ ์™„๋ฃŒ

์กฐ๊ฑด๋ณ„ ๋น„๊ต

์‹œ์Šคํ…œ ํ˜ธ์ถœ๋ณ„ ๋น„๊ต

ํ•ญ๋ชฉmprotect()pkey_mprotect()pkey_alloc()pkey_free()
์‹œ์Šคํ…œ ํ˜ธ์ถœ ๋ฒˆํ˜ธSYSCALL_DEFINE3SYSCALL_DEFINE4SYSCALL_DEFINE2SYSCALL_DEFINE1
pkey ๋งค๊ฐœ๋ณ€์ˆ˜-1 (์—†์Œ)์‚ฌ์šฉ์ž ์ง€์ •๋ฐ˜ํ™˜๊ฐ’ํ•ด์ œ ๋Œ€์ƒ
๋ฉ”๋ชจ๋ฆฌ ๋ณดํ˜ธ ํ‚ค๋ถˆ๊ฐ€๊ฐ€๋Šฅํ• ๋‹น๋งŒํ•ด์ œ๋งŒ
CONFIG_ARCH_HAS_PKEYS๋ถˆํ•„์š”ํ•„์š”ํ•„์š”ํ•„์š”
๋‚ด๋ถ€ ์ฒ˜๋ฆฌdo_mprotect_pkey(start,len,prot,-1)do_mprotect_pkey(start,len,prot,pkey)mm_pkey_alloc + arch_set_user_pkey_accessmm_pkey_free

PTE ๋ณดํ˜ธ ๋ณ€๊ฒฝ ์กฐ๊ฑด๋ณ„ ๋ถ„๊ธฐ

์กฐ๊ฑด์ฒ˜๋ฆฌ ๋ฐฉ์‹์ฝ”๋“œ ์œ„์น˜
pte_present && prot_numa && pte_protnone๊ฑด๋„ˆ๋œ€ (์ด๋ฏธ PROT_NONE)mprotect.c:248-249
pte_present && prot_numa && !folio_can_map_prot_numa๋ฐฐ์น˜ ๊ฑด๋„ˆ๋œ€mprotect.c:259-267
pte_present && !prot_numapte_modify() + writable ์‹œ๋„mprotect.c:269-299
pte_none && uffd_wp && userfaultfd_wp_use_markersPTE_MARKER_UFFD_WP ์„ค์น˜mprotect.c:308-318
pte_none && !uffd_wp๊ฑด๋„ˆ๋œ€mprotect.c:305-306
swap/migration entrysoftleaf ํƒ€์ž…๋ณ„ ์ฒ˜๋ฆฌmprotect.c:320-379
uffd_wp_resolve && pte_none markerpte_clear()mprotect.c:362-364

COW/์“ฐ๊ธฐ ๊ด€๋ จ ๋ถ„๊ธฐ

VMA ํƒ€์ž…์“ฐ๊ธฐ ๊ฐ€๋Šฅ ์กฐ๊ฑด์ฝ”๋“œ ์œ„์น˜
MAP_PRIVATE (๋น„๊ณต์œ )PageAnon && PageAnonExclusivemprotect.c:61-77
MAP_SHARED (๊ณต์œ )pte_dirty (์ด๋ฏธ ์“ฐ๊ธฐ๋จ)mprotect.c:79-95
MAP_PRIVATE + VM_LOCKED โ†’ writablepopulate_vma_page_range()๋กœ COW ํŠธ๋ฆฌ๊ฑฐmprotect.c:783-786
writenotify ํ•„์š”ํ•œ ๊ณต์œ  ๋งคํ•‘pte_clean์œผ๋กœ ์“ฐ๊ธฐ ๋ถˆ๊ฐ€ ์œ ์ง€mprotect.c:88-94

ํ•ต์‹ฌ ํ๋ฆ„: PTE ์“ฐ๊ธฐ ๋ณ€๊ฒฝ ์ตœ์ ํ™”

Linux 7.0์˜ mprotect๋Š” large folio์—์„œ ์—ฐ์†๋œ PTE๋ฅผ ๋ฐฐ์น˜(batching)๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚จ๋‹ค.

change_pte_range()
โ”œโ”€ mprotect_folio_pte_batch()        โ† large folio์—์„œ ์ฒ˜๋ฆฌํ•  PTE ์ˆ˜ ๊ณ„์‚ฐ
โ”‚    โ””โ”€ folio_pte_batch_flags()       โ† ์—ฐ์† PTE ์Šค์บ”
โ”œโ”€ modify_prot_start_ptes()          โ† ๋ฐฐ์น˜ ์‹œ์ž‘ (read-modify-write ํŠธ๋žœ์žญ์…˜)
โ”œโ”€ pte_modify()                      โ† ์ƒˆ ๋ณดํ˜ธ ์†์„ฑ ์ ์šฉ
โ”œโ”€ [MM_CP_TRY_CHANGE_WRITABLE]
โ”‚    โ””โ”€ set_write_prot_commit_flush_ptes()  โ† ์“ฐ๊ธฐ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ๊ฒฐ์ •
โ”‚         โ”œโ”€ [๊ณต์œ ] can_change_shared_pte_writable()
โ”‚         โ””โ”€ [๋น„๊ณต์œ ] commit_anon_folio_batch()
โ”‚              โ””โ”€ page_anon_exclusive_sub_batch()  โ† ํŽ˜์ด์ง€๋ณ„ exclusive ๊ฒ€์ฆ
โ””โ”€ modify_prot_commit_ptes()         โ† ๋ฐฐ์น˜ ์ปค๋ฐ‹ + TLB flush

๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • VMA / mmap
  • Huge Pages / THP
  • NUMA
  • Userfaultfd
  • Sealing (mseal)
  • mremap
  • Page Table Walk