WIL - PintOS:Project 3 / VM - Stack Growth

박상우·2024년 6월 29일
0

📝 TIL

목록 보기
41/45
post-thumbnail

⚽️ Main Goal

일반적으로 사용자 프로그램이 스택 포인터 아래에 메모리를 넣는 시도는 버그를 유발한다. 사용자 프로그램이 스택 포인터 아래에 메모리 쓰기를 시도하면 문제가 될 수 있다. 운영체제가 데이터를 스택에 저장하기 때문에 스택 포인터 아래에 쓴다면 시그널 처리중에 데이터가 덮어쓰이거나 스택의 데이터가 변경될 수 있다.

⇒ 애초에 잘못된 접근 ( 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

  • 스택 증가를 확인
    • 스택의 크기 : 1MB 제한
  • 증가 가능하다면, vm_stack_growth를 호출
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 구조체 수정

  • 유저 모드에서 커널모드로 돌아올때 USER STACK Pointer의 위치를 저장해야한다.
// 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.
profile
나도 잘하고 싶다..!

0개의 댓글