---
title: "Linux 샌드박스와 Fil-C: 메모리 안전성과 샌드박싱을 동시에"
published: 2025-12-13T22:58:29.000Z
canonical: https://jeff.news/article/820
---
# Linux 샌드박스와 Fil-C: 메모리 안전성과 샌드박싱을 동시에

메모리 안전 C/C++ 구현체 Fil-C에 OpenSSH의 seccomp-BPF 샌드박스를 포팅한 과정을 다룸. 메모리 안전성과 샌드박싱은 직교하는 개념이며 둘 다 필요함. GC 스레드 충돌 문제를 zlock_runtime_threads() API로 해결하고, seccomp 필터 3건만 수정하여 기존 보안 수준을 유지하면서 메모리 안전성을 확보함.

## 메모리 안전성과 샌드박싱은 별개의 개념임

- 메모리 안전성(memory safety)과 샌드박싱(sandboxing)은 직교(orthogonal)하는 개념임. 둘 중 하나만 있어도 되고, 둘 다 있을 수도 있음
- 순수 Java 프로그램은 메모리 안전하지만 샌드박스 없이 파일 시스템에 자유롭게 접근 가능함. 사용자 파일을 마음대로 덮어쓸 수 있어 위험함
- 반대로, 어셈블리로 작성된 프로그램이라도 OS에 모든 권한을 반납하면 메모리 버그가 있어도 파일 접근 자체가 불가능함
- 실제 샌드박스에는 구조적 구멍이 있음. 더 높은 권한을 가진 브로커 프로세스(broker process)에 메시지를 보낼 수 있기 때문에, 메모리 버그로 악성 메시지를 만들어 브로커를 공격하는 시나리오가 가능함

```mermaid
quadrantChart
    title 메모리 안전성 vs 샌드박싱
    x-axis "샌드박싱 없음" --> "샌드박싱 있음"
    y-axis "메모리 안전하지 않음" --> "메모리 안전함"
    quadrant-1 "최선: Fil-C + seccomp"
    quadrant-2 "Java 프로그램 (파일 접근 가능)"
    quadrant-3 "일반 C/C++ (가장 위험)"
    quadrant-4 "어셈블리 + 권한 반납"
```

## Fil-C란 무엇인가

- Fil-C는 C/C++의 메모리 안전 구현체(memory safe implementation)임. 일반적인 메모리 안전 언어와 달리 Linux syscall 레벨까지 안전성을 보장함
- init, udevd 같은 저수준 시스템 컴포넌트에서도 사용 가능할 만큼 런타임이 견고함
- OpenSSH를 포함한 다수의 프로그램이 Fil-C에서 동작함

## OpenSSH의 Linux 샌드박스 구조

- OpenSSH는 비특권 `sshd-session` 프로세스에 다음 4가지 기술을 적용함:
  - **chroot**: 파일 시스템 접근 범위 제한
  - **사용자/그룹 분리**: sshd 전용 사용자에게 권한 없음
  - **setrlimit**: 파일 열기, 프로세스 생성, 파일 쓰기 차단
  - **seccomp-BPF**: 허용된 syscall만 통과시키는 필터. 목록 외 syscall은 SIGSYS로 프로세스 종료

## 핵심 문제: Fil-C의 GC 스레드와 샌드박스 충돌

- Fil-C 런타임은 가비지 컬렉션(GC)용 백그라운드 스레드를 사용하며, 사용하지 않을 때 자동으로 종료했다가 필요 시 재시작함
- Linux에서 스레드는 경량 프로세스이므로, 스레드 생성은 OpenSSH의 "새 프로세스 금지" setrlimit 규칙을 위반함
- `clone3` 같은 syscall도 seccomp 허용 목록에 없음

## 해결책: zlock_runtime_threads() API

- `<stdfil.h>`에 새 API `zlock_runtime_threads()`를 추가함
- 이 함수를 호출하면 런타임이 필요한 스레드를 즉시 모두 생성하고, 이후 자동 종료/재시작을 비활성화함
- OpenSSH의 `ssh_sandbox_child` 함수에서 setrlimit이나 seccomp-BPF 호출 전에 이 함수를 먼저 호출하도록 수정함

## seccomp 필터 수정사항 (3건만 변경)

- **SECCOMP_RET_KILL_PROCESS**: 기존 `SECCOMP_RET_KILL`(해당 스레드만 종료) 대신 사용. 샌드박스 위반 시 Fil-C 백그라운드 스레드도 함께 종료시킴
- **MAP_NORESERVE**: mmap 허용 목록에 추가. Fil-C 메모리 할당기가 사용하며, 공격자에게 의미 있는 권한이 아님
- **sched_yield**: Fil-C 런타임 락 구현에 사용됨. 의미상 no-op에 가까운 syscall이라 보안 영향 없음
- futex 관련 syscall은 기존 필터에 이미 포함되어 있어 변경 불필요했음

## prctl 래핑: 모든 스레드에 필터 적용하기

- seccomp 필터 설치에 사용되는 `prctl` syscall은 호출한 스레드에만 적용됨. 백그라운드 스레드에는 적용되지 않는 문제가 있음
- Fil-C는 `prctl`을 래핑하여 내부 API `filc_runtime_threads_handshake()`로 모든 런타임 스레드에서 콜백을 실행함
- `PR_SET_NO_NEW_PRIVS`와 `PR_SET_SECCOMP` 모두 이 핸드셰이크를 통해 전체 스레드에 설치됨
- 복수의 사용자 스레드가 있을 경우 Fil-C 안전성 오류(safety error)를 발생시켜 모호한 상황을 방지함

## 핵심 포인트

- 메모리 안전성만으로는 부족하고, 샌드박싱만으로도 부족함. 둘을 조합해야 진정한 방어가 됨
- Fil-C + OpenSSH seccomp 조합에서 필요했던 변경은 최소한이었음: API 1개 추가, seccomp 필터 3건 수정, prctl 래핑 1건
- 기존 샌드박스의 보안 수준을 전혀 후퇴시키지 않으면서 메모리 안전성을 확보한 사례임

## 핵심 포인트

- 메모리 안전성과 샌드박싱은 직교 개념으로, 둘 다 갖추는 것이 최선의 방어임
- Fil-C의 GC 백그라운드 스레드가 OpenSSH의 프로세스 생성 금지 규칙과 충돌하여 zlock_runtime_threads() API를 추가함
- seccomp 필터 변경은 SECCOMP_RET_KILL_PROCESS, MAP_NORESERVE, sched_yield 3건뿐이었음
- prctl 래핑으로 filc_runtime_threads_handshake를 통해 모든 스레드에 seccomp 필터를 적용함

## 인사이트

메모리 안전 언어를 쓰더라도 샌드박싱 없이는 파일 시스템 접근 같은 위협을 막을 수 없고, 샌드박싱만으로는 브로커 프로세스를 통한 우회가 가능함. Fil-C가 최소한의 변경으로 OpenSSH 샌드박스를 통합한 점은 메모리 안전 C/C++의 실용성을 보여주는 사례임.
