1P by GN⁺ | ★ favorite | 댓글 1개
  • unsafe의 존재만으로 Rust를 배제하고 Fil-C만 안전하다고 보는 기준은 실제 소프트웨어의 적용 범위와 기술적 절충을 놓침
  • Fil-C는 C/C++의 잘못된 메모리 접근을 패닉으로 바꾸지만, ABI 비호환성, 일부 상황에서 수배의 성능 저하, GC 도입이라는 비용이 따름
  • Android의 약 500만 줄 Rust 코드에서는 출시 전 수정된 잠재적 메모리 안전성 취약점 1건이 발견돼 100만 줄당 0.2건으로 추산됐으며, C/C++의 과거 데이터인 약 1,000건보다 1,000배 이상 낮았음
  • 모든 프로그램에서 문제의 99.9%를 막는 기술과 90%의 프로그램에서 100%를 막는 기술 중 하나만 고를 필요는 없으며, Fil-C의 제약을 수용하기 어려운 소프트웨어에는 Rust 같은 대안이 적합함
  • 메모리 안전성은 성능·ABI·GC·데이터 경쟁 방지를 함께 고려해야 하며, Rust도 불충분하다고 비판한다면 일반 C/C++와 비 Fil-C Zig에는 적어도 같은 기준을 적용해야 함

Rust와 기존 시스템 언어의 책임 모델

  • 비 GC 시스템 프로그래밍 언어의 메모리 안전성 논의는 주로 Rust와 C/C++/Zig의 책임 모델 차이를 중심으로 진행돼 왔음
    • Rust는 안전할 수도 있는 일부 프로그램까지 거부하는 비용을 감수하면서, 메모리 안전성 문제를 일으킬 수 있는 프로그램의 컴파일을 막으려 함
    • unsafe는 원시 포인터 역참조 등을 허용해 일부 보장을 우회하는 탈출구임
    • C 계열 언어는 메모리 안전성 보장을 대부분 프로그래머에게 맡김
  • C++의 RAII와 스마트 포인터, Zig의 defer처럼 언어별 지원 수준에는 차이가 있지만, 잘못된 메모리 접근 자체를 원칙적으로 차단하지는 않음

Fil-C가 추가한 선택지

  • Fil-C는 C와 C++ 코드를 메모리 안전하게 실행하는 새로운 접근을 제공함
    • 범위 밖 접근이나 해제 후 사용처럼 잘못된 메모리 접근이 발생하면 패닉을 일으킴
    • GC와 포인터가 접근할 수 있는 메모리를 추적하는 InvisiCaps를 결합함
  • Zig에도 Fil-C에서 영감을 받은 새로운 컴파일 모드가 제안됨
  • 인기 C/C++ 프로젝트 일부가 Fil-C로 컴파일한 릴리스를 제공한다면 메모리 안전성 취약점을 줄일 선택지가 늘어날 수 있음

Rust를 안전하지 않다고 보는 기준

  • Fil-C 개발자는 Twitter에서 unsafe로 일부 보장을 우회할 수 있다는 이유로 Rust를 메모리 안전하지 않은 언어라고 평가해 왔음
  • Zig 개발자 Andrew Kelley도 관련 이슈 제목에서 Fil-C에 영감을 받은 모드를 “Rust와 달리 실제로 메모리 안전한” 컴파일 모드라고 표현함
  • 일부 논의는 Rust 사용자가 진정으로 메모리 안전성을 중시한다면 Rust를 버리고 더 안전한 Fil-C를 홍보해야 한다고 요구함
  • 이 기준은 Fil-C의 현실적 비용을 제외한 채 Rust와 Fil-C를 비교하며, Rust 커뮤니티가 종종 받는 광신적이라는 비판과 유사한 태도를 보임

Fil-C의 적용 제약

  • Fil-C는 비용 없는 드롭인 대체재가 아님
    • 비 Fil-C로 컴파일된 프로그램과 ABI가 호환되지 않음
    • 일부 상황에서는 수배 느릴 수 있음
    • GC를 도입함
  • 단순 유틸리티처럼 성능 저하를 체감하기 어렵거나 동적 링크가 필요 없는 프로그램에는 이런 제약이 결정적이지 않을 수 있음
  • 반대로 GC와 ABI 비호환성을 받아들일 수 없는 인기 프로젝트도 많으며, 현재 형태의 Fil-C를 적용하기 어려운 프로그램은 종종 Rust에 잘 맞음

실제 Rust 코드의 취약점 데이터

  • Rust의 실질적인 보안성을 판단할 데이터는 아직 많지 않지만, Rust 소프트웨어에서 악용 가능한 메모리 안전성 취약점이 많이 발견되지는 않았음
  • Android의 500만 줄 이상 Rust 코드에서는 잠재적 메모리 안전성 취약점 1건이 발견됐고 출시 전에 수정됨
    • 추산 취약점 밀도는 100만 줄당 0.2건
    • Android의 과거 C/C++ 데이터는 100만 줄당 약 1,000건임
    • Rust 코드의 밀도는 C/C++보다 1,000배 이상 낮게 추적되고 있음
  • 프로젝트마다 수치는 달라질 수 있지만, 실제 환경에서 Rust가 메모리 안전성 문제의 도입 위험을 크게 낮춘다는 근거가 됨

하나만 선택할 필요가 없는 이유

  • 모든 프로그램에서 문제의 99.9%를 막는 기술과 90%의 프로그램에서 문제의 100%를 막는 기술을 비교하는 가상의 선택은 적용 범위와 예방 수준이 모두 중요함을 보여줌
  • 실제 비율은 알 수 없지만 두 접근 가운데 하나만 선택할 필요는 없음
    • 절충을 수용할 수 있는 C/C++/Zig 프로젝트는 Fil-C 바이너리를 제공할 수 있음
    • Fil-C를 사용할 수 없는 소프트웨어는 메모리 안전성 취약점의 위험을 완전히 또는 대부분 제거하는 언어로 작성할 수 있음

GC를 쓸 수 있어도 Rust를 선택하는 이유

  • Go나 Fil-C 같은 GC 기반 선택지가 있어도 Rust를 사용하는 것은 타당함
  • GC 기반 언어로 작성할 수 있는 프로그램은 흔히 unsafe가 필요 없고, unsafe가 필요한 프로그램은 흔히 GC를 사용할 수 없음
  • 작은 메모리 안전성 위험보다 데이터 경쟁 방지 같은 다른 언어 보장과 기능을 더 중요하게 평가할 수 있음
  • Fil-C는 기존 C/C++의 메모리 안전성 취약점을 크래시로 바꿈
    • 보안 취약점보다는 낫지만, 100만 줄당 약 1,000건의 과거 밀도가 그대로라면 수정해야 할 크래시가 많이 남음
    • 과거에는 공격자가 프로그램을 크래시할 수 있는 능력을 이용한 보안 취약점도 있었음

일관된 메모리 안전성 기준

  • Rust의 100만 줄당 0.2건조차 허용할 수 없다면 일반 C/C++와 비 Fil-C Zig에도 같거나 더 엄격한 비판을 적용해야 함
  • Rust보다 안전성이 낮은 대안을 허용하면서 Rust만 unsafe 때문에 배제하는 태도는 메모리 안전성 절대주의를 일관되게 적용하지 못함

댓글과 토론

Lobste.rs 의견들
  • OP가 불쾌하게 받아들인 듯한 Andrew Kelly의 발언은 Zig가 C/C++ 의존성 전체까지 탈출구 없이 완전한 메모리 안전 실행 파일로 컴파일할 수 있으며, 포인터 추적 빈도에 따라 성능 비용이 약 1~6배라는 내용임
    글쓴이는 이를 Rust 공격으로 받아들이고 글 말미에는 Zig를 공격하지만, Fil-C와 Zig의 새 빌드 모드는 생태계에 긍정적인 기여이며 Rust와 다른 설계 지점과 절충점을 제공함
    Zig 팀이 선호하는 데이터 지향 프로그래밍을 따르면 성능 비용을 1배에 가깝게 줄일 수 있다는 의미로 해석함

    • 글쓴이는 “Fil-C에서 영감을 받은, Rust와 달리 실제로 메모리 안전한 컴파일 모드 도입”이라는 제목과 Fil-C 개발자의 Twitter 글에 대응한 것임
      이를 불필요하게 도발적이라고 읽는 것도 무리는 아니며, Andrew는 이후 제목을 덜 자극적으로 바꿨음
  • 평소와 반대로 “너희 언어는 메모리 안전하지 않다”는 가벼운 조롱이 향하자 Rust 개발자들의 반응 규모가 상당했음
    나도 Rust를 무척 좋아하지만 공평하게 받아들일 필요가 있음

    • “Rust는 상당히 메모리 안전하지만 Fil-C가 그 측면에서는 더 안전하다”는 말은 별로 논쟁적이지 않고 사실로 보임
      더 나아가 seL4의 형식 검증된 C는 한층 안전함
    • 올바른 프로그램을 중시하고 가능한 한 그렇지 않은 코드는 쓰지 않으려 하기에, 역설적으로 Zig보다 Rust를 선호함
      Zig는 메모리 할당 실패 시 올바르게 종료하기 쉽고 컴파일도 빠르다는 장점이 있음
      Fil-C가 Rust보다 더 메모리 안전한지는 모르겠지만 내 용도에서는 가비지 컬렉터와 C ABI 비호환성이 결정적인 걸림돌임
      단일 스레드로 만들면 경쟁 상태를 피하고 가비지 컬렉터를 넣으면 메모리 안전성을 얻을 수 있지만, Rust는 두 타협 없이 두 가지를 모두 제공하는 점이 좋음
  • 메모리 안전성을 매우 중시하므로 Fil-C와 Zig의 Fil-C ABI 구현은 당연한 선택임
    C 의존성이 있는 Rust 프로젝트는 안전성 보장이 약해지며, 순수 Rust만 쓰는 것도 가능하지만 불편함
    Rust 역시 Fil-C ABI를 구현해 C 의존성을 안전하게 빌드하고 Rust와 연결할 수 있어야 하며, 이것이 왜 논쟁적인지 모르겠음

    • Rust에는 안전하지 않은 Rust 코드가 안전한 Rust의 보장을 지키는지 검사하는 Miri 인터프리터가 이미 있음
      비슷한 기능을 구현한다면 디버그 빌드에서만 FFI 코드를 개선하는 보조 수단으로 쓰였으면 함
      가비지 컬렉터를 두고 모든 연산을 런타임에 검사하는 방식은 모든 용도에 맞지 않음
  • Fil-C를 단순한 유틸리티용으로 치부하기 전에 Software Should Work 콘퍼런스의 Fil-C 발표를 볼 필요가 있음
    발표자는 사용자 공간 전체와 OpenOffice Impress까지 Fil-C로 구성한 Linux 노트북으로 발표함
    일부 C/C++ 프로그램에는 적절하지 않겠지만, 글쓴이가 여기는 것만큼 장난감 기술로 보이지 않음

  • Python 출신이라 메모리 안전성은 기본 전제였고, Rust를 선택한 이유는 세 가지였음
    첫째, 강력한 타입 시스템으로 컴파일 시점 정확성을 확보할 수 있으며, #![forbid(unsafe_code)]cargo-geiger를 이용한 의존성 감사로 얻는 메모리 안전성은 그중 가장 덜 흥미로운 표현임
    둘째, 코드를 한 번 안전하게 작성해 여러 언어와 실행 환경에서 공유하기 쉬운 생태계를 제공함
    셋째, 당시의 try!(x)처럼 고수준 코드를 편하게 작성하게 해주는 문법 설탕이 있음
    Zig와 Fil-C는 타입 상태 패턴이나 새 타입 등으로 불변 조건을 타입 시스템에 인코딩해 논리 오류를 컴파일러가 잡게 하는 능력을 충족하지 못해 보임
    Fil-C의 ABI 비호환성도 공유 웹 호스팅의 CPython 같은 기존 런타임용 안전한 컴파일 모듈을 작성할 때 문제가 됨

    • Zig에서도 타입 상태 패턴과 다양한 제약 조건의 타입 인코딩이 가능하며, 어떤 면에서는 comptime이 Rust보다 표현력이 뛰어남
      컴파일 시간과 장황함, 생태계 대부분이 이 수준까지 시도하지 않는다는 절충점은 있지만 실제로 가능하고 꽤 재미있음
  • Fil-C 같은 기술이 왜 20년 전에는 등장하지 않았는지 궁금함

    • 메인프레임 분야에서는 포인터와 능력 기반 권한을 공유하고 태그하는 방식의 보안 문제를 수십 년간 경고해 왔음
      하지만 누구도 CPU나 메모리, 즉 비용으로 그 대가를 치르려 하지 않았음
    • 몇 달 전 Filip에게 비슷한 질문을 했고 다음과 같은 답변을 받았음
      2004~2018년에는 아이디어가 있었지만 메모리 안전한 C라는 발상 자체가 어리석다고 봤고, 2018~2023년에는 생각을 바꿨지만 극단적인 호환성을 달성할 방법을 찾지 못했음
      2023~2024년 초기 Fil-C는 호환성과 성능이 훨씬 낮았으며, 2024년 말 InvisiCaps의 돌파구로 현재의 높은 호환성과 괜찮은 성능을 얻었음
      2018년경 생각을 바꾼 계기는 GPU에서 쓰이는 C 변형들이 메모리 안전한 C의 단순한 형태라는 관찰이었음
    • 20년 전에는 컴퓨터가 더 느렸고 안전성 비용에 훨씬 민감했음
  • “Rust 진영이 정말 메모리 안전성을 중시한다면 더 안전한 Fil-C를 지지하고 Rust를 버려야 한다”는 말을 가장 선의로 해석하면, 이제 Fil-C가 있으니 세상을 Rust로 다시 쓰려는 노력을 중단하고 C/C++로 돌아가 예전처럼 통합된 라이브러리 생태계를 유지하자는 뜻임
    Rust 라이브러리를 C/C++에서 쓸 수 있어도 이를 원하지 않는 개발자들이 있으므로, Rust 사용자가 더 나은 해법을 인정하고 포기하면 분열이 사라진다는 논리임
    하지만 Fil-C에는 가비지 컬렉터와 x86-64 Linux 전용 지원이라는 Rust에 없는 절충점이 있음
    메모리 안전성 외에도 Cargo와 전역 이름공간이 없다는 점은 Rust를 쓸 중요한 이유이며, 언어 진영 간 분열이 더 넓은 문화 전쟁과 얽힌 상황은 안타까움
    누구나 기꺼이 쓸 라이브러리를 만들고 싶을 뿐임

    • C와 C++만으로도 이미 분절된 생태계 아닌지 의문임
      C 개발자 중에는 C++ 라이브러리를 원하지 않는 이들도 있을 수 있고, Zig와 Odin까지 등장했으므로 Rust가 사라져도 분절은 남음
      Rust가 특별히 다른 종류의 분절을 만드는 것인지 궁금함
    • 나 역시 누구나 만족하며 라이브러리를 쓰는 세상을 원하며, 제네릭을 comptime으로 보존하는 Rust→Zig 고수준 트랜스파일러를 개발 중임
      Rust는 더 많은 제약 조건을 인코딩하므로 원본 언어로 이상적임