Fil-C 메모리 안전 컨텍스트 스위칭

Fil-C 메모리 안전 컨텍스트 스위칭

Fil-C는 setjmp, longjmpucontext API(getcontext, setcontext, makecontext, swapcontext)의 메모리 안전 구현을 제공합니다. 이러한 메커니즘은 컨텍스트 스위칭 API의 오용으로 인해 스택 손상이나 Fil‑C 능력 모델 위반이 발생하지 않도록 보장하며, 특히 떠다니는 스택 복원을 방지합니다.

메모리 안전 setjmp와 longjmp

Fil‑C는 점프 버퍼를 불투명 객체로 취급하고 엄격한 스택‑프레임 검증을 적용함으로써 setjmplongjmp의 일반적인 메모리 안전 함정을 방지합니다.

표준 setjmp/longjmp의 위험

표준 C에서 setjmp는 레지스터 상태(칼리 저장 레지스터, 스택 포인터, 명령 포인터)를 저장하지만 스택 자체는 저장하지 않습니다. 이로 인해 두 가지 주요 안전 위험이 발생합니다:

  1. 떠다니는 스택: 프로그램이 setjmp를 호출한 뒤 해당 함수에서 반환하면 저장된 컨텍스트가 무효가 됩니다. 이후 그 컨텍스트로 longjmp를 수행하면 더 이상 존재하지 않는 스택 프레임을 복원하려 하게 되어 기계 상태가 손상됩니다.
  2. 컴파일러 최적화 오류: setjmp는 두 번 반환될 수 있기 때문에, 이를 호출하는 함수에 대해 컴파일러는 특정 최적화(예: 스필 슬롯 재사용)를 비활성화해야 합니다. 함수 포인터 등을 통해 setjmp 호출이 숨겨진 경우, 컴파일러가 이를 인식하지 못하고 스필 슬롯을 재사용하면 longjmp 후 변수들이 잘못된 값이나 쓰레기 값을 갖게 됩니다.

Fil‑C의 구현 전략

Fil‑C는 다음 메커니즘을 통해 이러한 위험을 완화합니다:

  • 불투명 점프 버퍼: jmp_buf는 Fil‑C 런타임이 전적으로 관리하는 불투명 zjmp_buf 객체에 대한 포인터를 포함합니다. 이를 통해 사용자가 점프 상태를 위조하거나 직접 조작하는 것을 방지합니다.
  • 컴파일러 강제: Fil‑C 컴파일러(filcc)는 setjmp가 직접 호출되어야 함을 요구합니다. 함수 포인터 등을 사용해 호출을 은폐하려 하면 컴파일 오류 또는 내부 컴파일러 오류(ICE)가 발생하여, 컴파일러의 returns_twice 논리가 항상 작동하도록 합니다.
  • 스택 프레임 등록: setjmp가 호출될 때 zjmp_buf를 할당하고 현재 스택 프레임에 등록합니다. 각 프레임은 유효한 zjmp_buf 대상들의 약한 집합을 유지합니다.
  • 조상 검증: longjmp는 현재 스택 프레임이 zjmp_buf를 만든 프레임의 하위 프레임이 아닌 경우 패닉을 일으킵니다(즉, 대상 프레임이 현재 호출 스택상의 조상이어야 함). 이는 스택을 순회하며 해당 프레임의 유효 집합에 zjmp_buf가 있는지 확인함으로써 검증됩니다.
  • GC 통합: zjmp_bufsetjmp 시점의 프레임 GC 루트 복사본을 저장합니다. zjmp_buf가 살아 있는 한 GC는 해당 루트를 계속 마크합니다.

메모리 안전 ucontext API

ucontext API 지원은 Fil‑C 0.680 릴리스에서 도입되었습니다. 이 API들은 실행 컨텍스트를 저장·복원함으로써 파이버와 코루틴을 구현하는 데 사용됩니다.

ucontext 상태 머신

오용을 방지하기 위해 Fil‑C는 zfiber_context 객체에 제한된 상태 머신을 구현합니다:

  • Uninitialized(초기화되지 않음): 초기 상태. getcontext 호출 또는 swapcontextfrom 인수로만 사용할 수 있습니다.
  • After_getcontext: getcontext 반환 후 상태. makecontext 호출 또는 swapcontextfrom 인수로만 사용할 수 있습니다.
  • Runnable: makecontext 후 또는 swapcontextfrom 인수로 사용된 후 상태. setcontext 호출 또는 swapcontextto 인수로만 사용할 수 있습니다.
  • Running: 컨텍스트가 현재 실행 중인 상태. swapcontext만 허용되며, 해당 컨텍스트가 현재 스레드에서 실행 중인 경우에만 가능합니다.

안전 제약 및 구현

  • 스택 관리: Fil‑C는 ucontext_t의 사용자 제공 ss_sp(스택 포인터)를 무시합니다. 대신 런타임이 makecontext 동안 내부적으로 관리되는 스택을 할당하여 Fil‑C의 오버플로우 처리와 호환되도록 합니다.
  • 스레드 친화성: ABI 일관성을 유지하기 위해 zfiber_context는 생성된 스레드를 추적합니다. 다른 스레드에서 컨텍스트로 전환하는 것을 금지하여 스택이 스레드 간에 이동하는 것을 방지합니다.
  • 불투명 상태: jmp_buf와 마찬가지로 ucontext_t는 내부 머신 상태를 사용자에게 숨기는 불투명 zfiber_context 객체에 대한 포인터를 포함합니다.

가비지 컬렉션과 파이버 스위칭

실시간 가비지 컬렉터와 파이버를 통합하려면 현재 스레드가 소유하지 않은 스택을 신중히 추적해야 합니다.

회색 파이버 문제

파이버가 runnable 상태(현재 실행 중이 아님)일 때 그 스택은 GC에 의해 스캔되어야 합니다. 그러나 변형기가 파이버로 전환한 뒤 GC 마크 단계 중에 다시 전환하면, GC가 이미 해당 파이버를 "black"(처리 완료)로 표시했을 경우 파이버 스택을 놓칠 수 있습니다.

Fil‑C는 회색 파이버를 추적함으로써 이를 해결합니다. 마크 단계 중에 swapcontext가 호출되면 전환 의 컨텍스트가 현재 스레드의 grey_fibers 리스트에 추가됩니다. GC 종료 직전 최종 스택 재스캔 시 스레드는 리스트에 있는 모든 회색 파이버의 스택을 순회하여 살아 있는 객체가 누락되지 않도록 합니다.

API 지원 요약

API Fil‑C 안전 메커니즘 주요 사용 사례
setjmp / longjmp 조상 검증 및 불투명 zjmp_buf 예외 처리, 신호 핸들러
getcontext / makecontext 상태 머신 및 관리 스택 파이버/코루틴 부트스트래핑
swapcontext 스레드 친화성 및 회색 파이버 GC 추적 파이버 컨텍스트 스위칭

요약

Fil‑C는 스택 손상과 떠다니는 스택 실행을 방지하기 위해 setjmp/longjmp와 ucontext API의 메모리 안전 버전을 구현하고, 이를 능력 모델 및 가비지 컬렉터와 통합합니다.

Fil-C 메모리 안전 컨텍스트 스위칭

Sources