Pintos Project 3 — Stack Growth 구현 흐름 & page_fault 분석

2025. 6. 13. 16:38개발

1. 왜 “자동 Stack Growth”가 필요한가?

  • 동적 스택 확장 : 사용자 프로그램은 pushcall, 재귀 등으로 스택을 예측 불가하게 사용.
  • 초기 스택 4 KiB 제한 만으로는 대부분 테스트/apps 불통 → fault 시 새 페이지를 할당해 스택을 확장해야 함.
  • 보안 & 메모리 보호 : 임의 주소 fault 를 모두 허용하면 공격 벡터. “합당한 범위” 만 grow.

2. GitBook 기준 Stack Growth 조건

  1. fault_addr 가 User 영역(< USER_STACK).
  2. fault_addr >= rsp − 8 (아래 §3 참고).
  3. 최대 깊이 1 MiB : USER_STACK − fault_addr ≤ 1 MiB.
  4. 중복 예약 안 됐는지 spt_find_page() 검사.

3. 왜 rsp − 8 Bytes 여유를 주나?

  • x86‑64 System V ABI : pushq Xrsp -= 8 후 [rsp]=X
  • call q 또한 리턴 주소 저장 전에 rsp -= 8.
  • 따라서 rsp 레지스터가 갱신되기 직전 메모리 접근이 먼저 일어나 fault 가능 → 최대 8바이트 범위 허용.

4. page_fault 핸들러 상세 플로(유저 fault)

// userprog/exception.c
static void page_fault(struct intr_frame *f) {
    void *fault_addr = rcr2();          // HW 등록값
    struct thread *t = thread_current();
    uint64_t rsp = is_kernel_vaddr(f->rsp)? t->stack_pointer : f->rsp; // 유저 rsp 확보

    bool not_present = (f->error_code & PF_P) == 0;
    bool success = false;
    if (not_present && is_user_vaddr(fault_addr)) {
        success = vm_try_handle_fault(fault_addr, rsp, f->rip);
    }
    if (!success) process_exit(-1);
}

vm_try_handle_fault() 중 스택 growth 분기

if (p == NULL) {
    /* Stack growth 조건 검사 */
    if (fault_addr >= rsp - 8 && fault_addr < USER_STACK
        && USER_STACK - fault_addr <= (1<<20)) {
        /* 1. 예약 */
        bool ok = vm_alloc_page_with_initializer(VM_ANON,
                          pg_round_down(fault_addr), true,
                          anon_initializer, NULL);
        /* 2. 즉시 claim (0‑fill) */
        success = ok && vm_claim_page(pg_round_down(fault_addr));
    }
}

5. 구현 단계 체크리스트

  1. setup_stack() : 최초 1 page 예약+claim 후 if_.rsp = USER_STACK.
  2. page_fault() 에 유저 rsp 전달(시스템콜 시 커널 rsp 보관).
  3. vm_try_handle_fault() 에 위 3가지 조건 구현.
  4. vm_alloc_page_with_initializer() 로 UNINIT 페이지 저장 → anon_initializer.
  5. anon_swap_in() : PAL_USER|PAL_ZERO 프레임 할당 · PTE 매핑.

6. 핵심 테스트

  • tests/vm/stck- growth-1 : fault 3 회, rsp –8 조건 검증.
  • tests/vm/stck- growth-2 : 1 MiB 초과 grow 시 kill 확인.
  • userprog 기존 테스트 args-n *  : argv 푸시용 추가 grow 경로.