Linux 샌드박스와 Fil-C
(fil-c.org)- 메모리 안전성과 샌드박싱은 서로 독립적인 보안 개념으로, 두 가지를 모두 갖춰야 강력한 방어 체계를 형성함
- Fil-C는 C/C++의 메모리 안전 구현체로, Linux 시스템 호출 수준까지 안전성을 보장하며 OpenSSH 같은 시스템 구성요소에서도 사용 가능함
- OpenSSH의 seccomp-BPF 기반 샌드박스를 Fil-C로 이식하는 과정에서, 스레드 생성 제한과 seccomp 필터 조정이 핵심 과제였음
- Fil-C 런타임의 백그라운드 스레드 관리를 위해
zlock_runtime_threads()API가 추가되어, 샌드박스 내에서 스레드 동작을 제어함 - Fil-C는
prctl호출을 모든 런타임 스레드에 동기화 적용해, no_new_privs 및 seccomp 필터가 전체 프로세스에 일관되게 적용되도록 구현함
메모리 안전성과 샌드박싱의 관계
- 메모리 안전성과 샌드박싱은 서로 다른 보안 계층으로, 한쪽만으로는 완전한 보호를 제공하지 않음
- 메모리 안전하지만 샌드박스되지 않은 예: Java 프로그램이 사용자 입력을 통해 파일을 덮어쓸 수 있는 경우
- 샌드박스되었지만 메모리 안전하지 않은 예: 어셈블리로 작성되어 권한이 제한된 프로그램
- 실제 샌드박스는 브로커 프로세스와의 통신 허용 등 구조적 허점이 존재
- 따라서 메모리 안전성과 샌드박싱을 병행하는 것이 최선의 방어 방식임
Fil-C와 OpenSSH 샌드박스의 결합
- Fil-C는 C/C++의 메모리 안전 구현체로, Linux 시스템 호출 수준에서 안전성을 유지
- Fil-C 런타임은
init,udevd같은 저수준 시스템 구성요소에서도 동작 가능 - OpenSSH는 Fil-C에서 정상적으로 작동하며, seccomp-BPF 샌드박스를 활용함
- Fil-C 런타임은
- OpenSSH는 Linux에서 다음 도구로 샌드박스를 구성함
-
chroot로 파일시스템 접근 제한 -
sshd사용자/그룹으로 실행 -
setrlimit으로 파일 열기·프로세스 생성 제한 - seccomp-BPF 필터로 허용된 시스템 호출만 허용
-
- Fil-C는
chroot와 사용자 전환은 기본적으로 지원하지만,setrlimit과 seccomp-BPF는 런타임 동작과 충돌 가능성이 있어 추가 조정 필요
Fil-C 런타임의 스레드 제어
- Fil-C 런타임은 가비지 컬렉션용 백그라운드 스레드를 사용하며, 필요 시 자동으로 중단·재시작함
- OpenSSH의
setrlimit샌드박스는 새 프로세스 생성 금지를 목표로 하므로, 런타임의 스레드 생성이 이를 위반할 수 있음 - 이를 해결하기 위해
<stdfil.h>에zlock_runtime_threads()API를 추가- 런타임이 필요한 스레드를 즉시 생성하고, 이후 자동 종료를 비활성화
- OpenSSH의
ssh_sandbox_child함수에서setrlimit또는 seccomp-BPF 호출 전에 실행
OpenSSH seccomp 필터 조정
-
zlock_runtime_threads()적용 후 대부분의 샌드박스 기능은 그대로 작동 - seccomp 필터에서 다음 변경이 이루어짐
- 위반 시
SECCOMP_RET_KILL_PROCESS로 설정해 Fil-C 백그라운드 스레드도 함께 종료 -
MAP_NORESERVE를 허용 목록에 추가, Fil-C 메모리 할당기 사용 지원 -
sched_yield호출 허용, Fil-C의 락 구현에서 사용됨
- 위반 시
- Fil-C의 동기화용
futex호출은 이미 허용되어 추가 변경 불필요
Fil-C의 prctl 구현 방식
- OpenSSH는 seccomp 필터 설치 시 두 가지
prctl호출을 사용-
PR_SET_NO_NEW_PRIVS로 추가 권한 획득 차단 -
PR_SET_SECCOMP, SECCOMP_MODE_FILTER로 필터 활성화
-
- 문제는
prctl이 호출 스레드에만 적용된다는 점으로, Fil-C의 다른 런타임 스레드가 필터 없이 남을 위험 존재 - Fil-C는 이를 방지하기 위해
filc_runtime_threads_handshake()API로 모든 런타임 스레드에 동기화 적용- 각 스레드가 동일한
prctl호출을 수행하도록 보장 - 여러 사용자 스레드가 존재할 경우 Fil-C 안전성 오류를 발생시켜 보호 강화
- 각 스레드가 동일한
결론
- 메모리 안전성과 샌드박싱의 결합이 가장 강력한 보안 조합
- Fil-C는 OpenSSH의 seccomp 기반 샌드박스를 완전하게 통합하면서도 보호 수준 저하 없이 메모리 안전성 유지
- Linux 환경에서 Fil-C를 활용하면, 시스템 수준 보안과 언어 수준 안전성을 동시에 확보 가능
Hacker News 의견들
-
왜 landlock에 대한 언급이 없는지 궁금함
-
C → WASM → C로 이어지는 하이브리드 컴파일 접근법이 있음
이렇게 하면 OS 상호작용을 완전히 제어하고, WASM처럼 메모리 접근을 샌드박싱하면서도 기술적으로는 여전히 C 코드로 유지됨
관련 자료는 RLBox에서 볼 수 있음- WASM 샌드박스는 프로그램의 정합성(soundness) 을 보장하지 않음
메모리를 망가뜨릴 수는 있지만, 그 범위가 샌드박스 안으로 제한될 뿐임
SECCOMP 같은 시스템은 모든 상호작용 정책을 세세히 정의해야 해서 관료적임
반면 Fil-C는 언어와 런타임 자체가 프로그램의 올바른 동작을 보장하려는 접근을 취함
Fil-C 바이너리는 일반 실행 파일이므로 SECCOMP 같은 샌드박스와 함께 사용할 수도 있음
Linux가 prctl 인터페이스를 만드는 데 20년이 걸렸으니, WASI에서 비슷한 걸 보려면 10년은 기다려야 할 것 같음 - RLBox는 샌드박싱 기술이지 메모리 안전성 기술은 아님
샌드박스 내부에서도 이상한 실행 흐름을 만들 수 있음
- WASM 샌드박스는 프로그램의 정합성(soundness) 을 보장하지 않음
-
Fil-C의 저자는 기술적으로 흥미로운 발명을 잘 하지만, 구현 검증이 충분히 이루어졌는지는 걱정됨
setuid 프로그램을 Fil-C로 컴파일할 수 있다고 했는데, ld.so를 수정한 부분이 위험할 수 있음
setuid 앱은 환경 변수, 파일 디스크립터, rlimit, 시그널 등에서 매우 방어적으로 작성되어야 함
이런 부분은 아직 미완성이라 실제 인프라에 쓰기엔 리스크가 있음
그래도 코드베이스를 Fil-C로 테스트해보면 흥미로운 버그를 발견할 수도 있음- 실제로 Fil-C를 깨뜨릴 방법이 있는지 테스트 중임
- 진짜 걱정된다면 직접 실험하고 결과를 공유해야 함
- 런타임을 투명하게 만들어 감사(auditing) 가 가능하도록 하는 게 목표임
ld.so 수정은 libc 레이아웃을 가르치는 정도의 작은 변경임
setuid 관련 getenv 버그도 이미 secure_getenv로 수정했음
지적한 내용엔 일부 진실과 일부 FUD가 섞여 있음 - Fil-C 저자가 비판에 비협조적으로 대응하는 태도가 아쉬움
Fil-C는 데이터 레이스 상황에서 포인터와 capability가 불일치할 수 있음
이로 인해 메모리 안전성 위반이 발생할 수 있음
저자는 이를 부정하지만, Java와 비교하는 건 부적절함
기술은 훌륭하지만, 저자의 태도가 신뢰를 떨어뜨림
-
WASM은 샌드박스이자 실행 환경으로, 사용 방식에 따라 메모리 안전성을 어느 정도 확보할 수 있음
C를 WASM으로 컴파일하면 버그는 여전히 존재하지만, 그 영향 범위가 제한됨
따라서 WASM을 샌드박싱 기술로 분류하는 건 맞지만, 실행 환경으로서 더 많은 가능성이 있음- WASM은 결국 샌드박싱 기술임
모듈 B의 버그로 모듈 A의 데이터를 읽을 수 있음
즉, WASM은 가벼운 프로세스 샌드박스 대체재에 불과함 - “사용 방식에 따라 다르다”는 말 자체가 WASM이 완전히 안전하지 않다는 증거임
C도 “사용 방식에 따라 안전하다”고 말할 수 있으니까 - WASM은 C의 메모리 모델을 이해하지 못하므로, Fil-C처럼 보호 기능을 구현할 수 없음
WASM은 런타임 탈출은 막지만, 내부 프로그램의 메모리 탈출은 막지 못함
- WASM은 결국 샌드박싱 기술임
-
Fil-C와 Rust 비교를 요청함
- 두 기술 모두 훌륭하지만 접근 방식이 다름
Fil-C는 기존 C 프로그램을 호환성과 보안 중심으로 강화하는 데 적합함
Rust는 새 코드베이스를 만들 때 정적 안전성과 성능을 확보하기 좋음
Fil-C는 약간의 성능 손실이 있지만 기존 C 코드(ffmpeg, nginx, sudo 등)에 유용함
Rust는 멀티스레딩과 타입 시스템이 강점임 - Fil-C는 GC를 도입해 성능 저하가 있을 수 있음
언어 설계 개선보다는 메모리 안전성 확보가 목적임
경쟁군은 Rust보다는 D, Nim, Go에 가까움 - Fil-C는 런타임 검사로 안전하지 않은 메모리 접근을 감지하면 프로그램을 중단시킴
Rust는 컴파일 타임에 예방함
두 접근은 직교적이며, Rust에도 Fil-C 스타일의 런타임 검사를 추가할 수 있음
- 두 기술 모두 훌륭하지만 접근 방식이 다름
-
MicroVM이 점점 인기를 얻고 있음
Fil-C가 이를 어떻게 활용할 수 있을지 궁금함- 약간의 포팅이 필요하겠지만, microVM의 일부 기능을 Fil-C의 메모리 안전한 유저랜드로 끌어올릴 수도 있음
-
이 프로젝트가 더 주목받길 바람
sudo나 polkit 같은 핵심 도구가 메모리 안전하게 배포되면 좋겠음 -
메모리 안전 언어에서도 샌드박싱 활용이 더 많아졌으면 함
Rust나 Go에서도 OS 수준의 샌드박스를 잘 안 쓰는 게 아쉬움-
Seccomp는 이식성과 조합성이 떨어짐
라이브러리 단위로 설정하기 어렵고, 커널 버전이나 libc 구현에 민감함
파일 경로 같은 포인터 뒤의 입력은 필터링할 수 없어 한계가 있음
그래서 보통 애플리케이션 단위로 직접 설정해야 함 - Rust는 런타임이 거의 없어서 샌드박싱에 적합함
반면 Go는 런타임이 커서 Fil-C처럼 안전하게 만들기 어려움
-
Seccomp는 이식성과 조합성이 떨어짐
-
Fil-C가 clang의 Address Sanitizer(ASan) 와 뭐가 다른지 궁금함
단순히 런타임 패닉을 내는 수준이라면 “메모리 안전”이라 부르기 어렵지 않나 함- ASan은 버그를 잘 잡지만 완전한 메모리 안전성을 보장하지 않음
일부 버그는 탐지하지 못함
메모리 주변에 “red zone”을 두는 방식이라 운이 좋으면 탐지됨 - 메모리 오류가 영향을 주기 전에 패닉으로 막힌다면, 그것도 메모리 안전으로 볼 수 있음
메모리 안전은 “크래시하지 않는다”가 아니라 “잘못된 접근이 효과를 내지 못한다”는 의미임
- ASan은 버그를 잘 잡지만 완전한 메모리 안전성을 보장하지 않음
-
완전한 VM을 샌드박스로 쓰면 안 되는 이유를 질문함
- VM은 훌륭한 샌드박스지만, Chrome이나 OpenSSH 같은 앱은 권한 분리가 필요함
하나의 프로세스는 권한 없이 입력을 파싱하고, 다른 프로세스는 높은 권한으로 동작함
두 프로세스는 IPC로 통신함
VM을 쓰면 보안은 높지만 오버헤드가 크고, GPU나 파일 접근 같은 기능이 복잡해짐
그래서 일반적으로는 OS 수준의 샌드박싱이 더 깔끔함 - VM은 대부분의 문제를 해결하지만, 데스크톱 그래픽 가속은 여전히 어려움
GPU를 전용으로 할당해야 하고, Qubes도 X11 포워딩으로만 연결되어 가속이 없음
- VM은 훌륭한 샌드박스지만, Chrome이나 OpenSSH 같은 앱은 권한 분리가 필요함