일반적으로 사용자 프로그램이 스택 포인터 아래에 메모리를 넣는 시도는 버그를 유발한다. 사용자 프로그램이 스택 포인터 아래에 메모리 쓰기를 시도하면 문제가 될 수 있다. 운영체제가 데이터를 스택에 저장하기 때문에 스택 포인터 아래에 쓴다면 시그널 처리중에 데이터가 덮어쓰이거나 스택의 데이터가 변경될 수 있다.
⇒ 애초에 잘못된 접근 ( CASE 2 )
rsp >= USER_STACK &&
addr >= rsp &&
addr(메모리 쓰기 위치) <= USER_STACK
하지만 PintOS에서 사용하는 x86-64아키텍처의 Push 명령어는 스택 포인터를 조절하기 전에 접근 권한을 먼저 검사해서, Page Fault를 발생시킨다. OS는 이를 감지하고 스택을 확장할 수 있다.
⇒ rsp - 8에 해당하는 메모리의 Page Fault 여부 판단, 페이지 삽입 ( CASE 1 )
addr <= USER_STACK &&
rsp - 8 <= USER_STACK &&
addr(메모리 쓰기 위치) == rsp(스택 포인터) - 8
그리고
rsp를 위 두 경우에 대한 조건문을 만들어 처리하기 위해서 유효한 rsp를 알아야한다.
rsp는 pagefault가 발생했을 때 인자로 받는 `if` 를 통해서 확인할 수 있다.
하지만 pagefault는 커널에서도, 사용자 페이지에서도 발생할 수 있기 때문에, 모든 경우에서 `if -> rsp` 를 사용하는 것은 바람직하지 않다.
유저 모드에서 커널 모드로 전환되는 경우 rsp를 thread에 저장해서 그 값을 사용해야한다.
vm_try_handle_fault
bool vm_try_handle_fault (struct intr_frame *f UNUSED, void *addr UNUSED,
bool user UNUSED, bool write UNUSED, bool not_present UNUSED) {
// addr 주소 유효성 검사
if (addr == NULL || is_kernel_vaddr(addr))
return false;
/* Page Fault 원인을 검사 */
if ( not_present ) {
struct thread *t = thread_current();
void * rsp = !user ? t -> utok_rsp : f -> rsp;
// 스택 확장 관련 Stack Growth 처리
if (addr <= USER_STACK && rsp - 8 >= USER_STACK - MAX_STACK_SIZE && addr == rsp - 8) {
vm_stack_growth(addr);
}
else if ( rsp >= USER_STACK - MAX_STACK_SIZE && addr >= rsp && addr <= USER_STACK ) {
vm_stack_growth(addr);
}
/* Supplemetal Page Table 참조 */
struct supplemental_page_table *spt UNUSED = &thread_current ()->spt;
/* Page Fault 대상 page 컨텐츠 load */
struct page *page = spt_find_page(spt, addr);
if (page == NULL)
return false;
/* write 불가능한 페이지에 접근한 경우 */
if (write == 1 && page -> writable == 0)
return false;
/* 페이지 테이블 갱신 */
return vm_do_claim_page (page);
}
return false;
}
vm_stack_growth
VM_ANON
페이지를 통해 스택의 크기를 늘림PGSIZE
기준 주소를 내림하여 해당 위치에 페이지 삽입/* Growing the stack. */
static void
vm_stack_growth (void *addr UNUSED) {
void *ordered_addr = pg_round_down(addr); // 현재 주소값을 페이지 단위에 맞게 정렬
vm_alloc_page(VM_ANON | VM_MARKER_0, ordered_addr, 1); // 스택에 넣는 ANON Page임을 확인하기 위해 보조 타입 VM_MARKER_0을 활용한다.
}
// thread.h
struct thread {
...
#ifdef VM
struct supplemental_page_table spt;
**void *utok_rsp;**
#endif
...
}
pass tests/userprog/args-none
pass tests/userprog/args-single
pass tests/userprog/args-multiple
pass tests/userprog/args-many
pass tests/userprog/args-dbl-space
pass tests/userprog/halt
pass tests/userprog/exit
pass tests/userprog/create-normal
pass tests/userprog/create-empty
pass tests/userprog/create-null
pass tests/userprog/create-bad-ptr
pass tests/userprog/create-long
pass tests/userprog/create-exists
pass tests/userprog/create-bound
pass tests/userprog/open-normal
pass tests/userprog/open-missing
pass tests/userprog/open-boundary
pass tests/userprog/open-empty
pass tests/userprog/open-null
pass tests/userprog/open-bad-ptr
pass tests/userprog/open-twice
pass tests/userprog/close-normal
pass tests/userprog/close-twice
pass tests/userprog/close-bad-fd
pass tests/userprog/read-normal
pass tests/userprog/read-bad-ptr
pass tests/userprog/read-boundary
pass tests/userprog/read-zero
pass tests/userprog/read-stdout
pass tests/userprog/read-bad-fd
pass tests/userprog/write-normal
pass tests/userprog/write-bad-ptr
pass tests/userprog/write-boundary
pass tests/userprog/write-zero
pass tests/userprog/write-stdin
pass tests/userprog/write-bad-fd
pass tests/userprog/fork-once
pass tests/userprog/fork-multiple
pass tests/userprog/fork-recursive
pass tests/userprog/fork-read
pass tests/userprog/fork-close
pass tests/userprog/fork-boundary
pass tests/userprog/exec-once
pass tests/userprog/exec-arg
pass tests/userprog/exec-boundary
pass tests/userprog/exec-missing
pass tests/userprog/exec-bad-ptr
pass tests/userprog/exec-read
pass tests/userprog/wait-simple
pass tests/userprog/wait-twice
pass tests/userprog/wait-killed
pass tests/userprog/wait-bad-pid
pass tests/userprog/multi-recurse
pass tests/userprog/multi-child-fd
pass tests/userprog/rox-simple
pass tests/userprog/rox-child
pass tests/userprog/rox-multichild
pass tests/userprog/bad-read
pass tests/userprog/bad-write
pass tests/userprog/bad-read2
pass tests/userprog/bad-write2
pass tests/userprog/bad-jump
pass tests/userprog/bad-jump2
pass tests/vm/pt-grow-stack
pass tests/vm/pt-grow-bad
pass tests/vm/pt-big-stk-obj
pass tests/vm/pt-bad-addr
pass tests/vm/pt-bad-read
pass tests/vm/pt-write-code
pass tests/vm/pt-write-code2
pass tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
pass tests/vm/page-parallel
pass tests/vm/page-merge-seq
FAIL tests/vm/page-merge-par
FAIL tests/vm/page-merge-stk
FAIL tests/vm/page-merge-mm
pass tests/vm/page-shuffle
FAIL tests/vm/mmap-read
FAIL tests/vm/mmap-close
FAIL tests/vm/mmap-unmap
FAIL tests/vm/mmap-overlap
FAIL tests/vm/mmap-twice
FAIL tests/vm/mmap-write
pass tests/vm/mmap-ro
FAIL tests/vm/mmap-exit
FAIL tests/vm/mmap-shuffle
FAIL tests/vm/mmap-bad-fd
FAIL tests/vm/mmap-clean
FAIL tests/vm/mmap-inherit
FAIL tests/vm/mmap-misalign
FAIL tests/vm/mmap-null
FAIL tests/vm/mmap-over-code
FAIL tests/vm/mmap-over-data
FAIL tests/vm/mmap-over-stk
FAIL tests/vm/mmap-remove
pass tests/vm/mmap-zero
FAIL tests/vm/mmap-bad-fd2
FAIL tests/vm/mmap-bad-fd3
FAIL tests/vm/mmap-zero-len
FAIL tests/vm/mmap-off
FAIL tests/vm/mmap-bad-off
FAIL tests/vm/mmap-kernel
FAIL tests/vm/lazy-file
pass tests/vm/lazy-anon
FAIL tests/vm/swap-file
FAIL tests/vm/swap-anon
FAIL tests/vm/swap-iter
FAIL tests/vm/swap-fork
pass tests/filesys/base/lg-create
pass tests/filesys/base/lg-full
pass tests/filesys/base/lg-random
pass tests/filesys/base/lg-seq-block
pass tests/filesys/base/lg-seq-random
pass tests/filesys/base/sm-create
pass tests/filesys/base/sm-full
pass tests/filesys/base/sm-random
pass tests/filesys/base/sm-seq-block
pass tests/filesys/base/sm-seq-random
FAIL tests/filesys/base/syn-read
pass tests/filesys/base/syn-remove
FAIL tests/filesys/base/syn-write
pass tests/threads/alarm-single
pass tests/threads/alarm-multiple
pass tests/threads/alarm-simultaneous
pass tests/threads/alarm-priority
pass tests/threads/alarm-zero
pass tests/threads/alarm-negative
pass tests/threads/priority-change
pass tests/threads/priority-donate-one
pass tests/threads/priority-donate-multiple
pass tests/threads/priority-donate-multiple2
pass tests/threads/priority-donate-nest
pass tests/threads/priority-donate-sema
pass tests/threads/priority-donate-lower
pass tests/threads/priority-fifo
pass tests/threads/priority-preempt
pass tests/threads/priority-sema
pass tests/threads/priority-condvar
pass tests/threads/priority-donate-chain
FAIL tests/vm/cow/cow-simple
34 of 141 tests failed.