Fil-C 메모리 안전 컨텍스트 스위칭
Fil-C 메모리 안전 컨텍스트 스위칭
Fil-C는 setjmp, longjmp 및 ucontext API(getcontext, setcontext, makecontext, swapcontext)의 메모리 안전 구현을 제공합니다. 이러한 메커니즘은 컨텍스트 스위칭 API의 오용으로 인해 스택 손상이나 Fil‑C 능력 모델 위반이 발생하지 않도록 보장하며, 특히 떠다니는 스택 복원을 방지합니다.
메모리 안전 setjmp와 longjmp
Fil‑C는 점프 버퍼를 불투명 객체로 취급하고 엄격한 스택‑프레임 검증을 적용함으로써 setjmp와 longjmp의 일반적인 메모리 안전 함정을 방지합니다.
표준 setjmp/longjmp의 위험
표준 C에서 setjmp는 레지스터 상태(칼리 저장 레지스터, 스택 포인터, 명령 포인터)를 저장하지만 스택 자체는 저장하지 않습니다. 이로 인해 두 가지 주요 안전 위험이 발생합니다:
- 떠다니는 스택: 프로그램이
setjmp를 호출한 뒤 해당 함수에서 반환하면 저장된 컨텍스트가 무효가 됩니다. 이후 그 컨텍스트로longjmp를 수행하면 더 이상 존재하지 않는 스택 프레임을 복원하려 하게 되어 기계 상태가 손상됩니다. - 컴파일러 최적화 오류:
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_buf는setjmp시점의 프레임 GC 루트 복사본을 저장합니다.zjmp_buf가 살아 있는 한 GC는 해당 루트를 계속 마크합니다.
메모리 안전 ucontext API
ucontext API 지원은 Fil‑C 0.680 릴리스에서 도입되었습니다. 이 API들은 실행 컨텍스트를 저장·복원함으로써 파이버와 코루틴을 구현하는 데 사용됩니다.
ucontext 상태 머신
오용을 방지하기 위해 Fil‑C는 zfiber_context 객체에 제한된 상태 머신을 구현합니다:
- Uninitialized(초기화되지 않음): 초기 상태.
getcontext호출 또는swapcontext의from인수로만 사용할 수 있습니다. - After_getcontext:
getcontext반환 후 상태.makecontext호출 또는swapcontext의from인수로만 사용할 수 있습니다. - Runnable:
makecontext후 또는swapcontext의from인수로 사용된 후 상태.setcontext호출 또는swapcontext의to인수로만 사용할 수 있습니다. - 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의 메모리 안전 버전을 구현하고, 이를 능력 모델 및 가비지 컬렉터와 통합합니다.