IT/시스템 보안

[DirtyCOW] memory mapping, mmap(), Copy on Write

kykyky 2024. 5. 14. 19:41

memory mapping

 

memory mapping이 뭔가요?

 

일반적으로 file에 access(open, read, write, ...)하는 방식은 두 가지가 있다.

 

1) by I/O Operation

파일에 접근할 때마다 매번 system call을 통해 kernel을 깨워야 함

-> overhead가 발생해버린다.

 

2) by memory mapping

출처: https://w3.cs.jmu.edu/kirkpams/OpenCSF/Books/csf/html/MMap.html

처음에 파일의 내용을 프로세스의 메모리에 한번 mapping해 두면,
이후에 파일 내용에 접근할 때는, system call이 아니라, 메모리 주소를 통해 직접 접근 
-> overhead 최소화!

 

memory mapping by mmap() system call

void *mmap(

void addr[.length], 

시작 주소: 어디에다가부터 mapping할지

 

size_t length,         

mapped memory의 크기

 

int prot,                   

해당 memory가 readable이나 writable인지. -> 이전의 open()의 mode와 일치해야 함

 

int flags,                 

▶ MAP_SHARED: 다른 프로세스도 이 mapped memory를 사용 -> shared memory

- 이 mapped memory에서의 변화는 다른 프로세스에게도 보임

                               

MAP_PRIVATE: calling 프로세스만 이 mapped memory를 사용 -> 다른 프로세스가 수정하려면 COW 

- 이 mapped memory에서의 변화는 다른 프로세스에게는 안 보임

 

int fd,                       

map될 file

 

off_t offset               

map될 file 내에서 정확히 어디에서부터 mapping을 시작할 것인지

); 

 

반환값: mapped memory의 시작 주소 

 

read & write on mapped memory by memcpy()

 

 

Copy on Write

발생하는 상황

- 한 프로세스가 map한 memory를 다른 프로세스가 write하려 할 때

▶process Q가 page 3에 map해놨는데

▶process P가 여기에 write(): (단지 read만 할 거면 COW는 일어나지 않는다.)
exception이 발생한 뒤,
OS는 새 physical memory (: dirty page)를 할당하고,
page 3을 dirty page에 복사하고,
page table이 변경돼 virtual memory는 복사본을 가리키게 되며,
process P는 이제 이것을 수정하게 됨 

▶그 뒤 madvise():
이것의 3번째 인자가 MADV_DONOTNEED라면,
복사본 memory는 free되고, page table가 변경돼 virtual memory는 원본을 가리킴

 

- read-only 파일이 map된 memory를 write하려 할 때

: 여기서도 위 상황처럼 write()와 madvise()가 수행되는 것은 동일하다.

 

예시

fork()

 

DirtyCOW vulnerability

 

위 Copy on Write의 과정 설명처럼, write()와 madvise()가 순서를 지켜 일어나면 참 좋겠지만,

안타깝게도 write()의 작동이 atomic하지 않고 각 단계가 분리되어 있기에,

madvise()와 race condition을 발생시킬 수 있다.

 

만약 위 그림과 같이, 

write()에서 기껏 page table을 변경해 virtual memory가 안전하게 복사본을 가리키게 했더라도,

madvise()가 그새를 못기다리고 이때 끼어들어 다시 page table을 변경해 virtual memory가 'read-only인 원본' (혹은 '다른 프로세스의 private memory')을 가리키게 해놓는다면,

write()는 그런 줄도 모르고 원본 (혹은 pritvate memory)에다가 write를 수행하게 되는 것이 DirtyCOW이다.