1P by neo 7달전 | favorite | 댓글 1개

Hubris IPC의 개요

  • Hubris는 작고 애플리케이션에 의존적이지 않은 커널을 사용하며, 대부분의 코드(드라이버, 애플리케이션 로직, 네트워크 스택 등)는 별도로 컴파일된 격리된 태스크에 존재함
  • 이러한 태스크들은 크로스-태스크 메시징 시스템(IPC)을 사용하여 서로 통신할 수 있음
  • Hubris의 IPC는 커널에 구현된 3가지 핵심 연산(RECV, SEND, REPLY)으로 구성됨
    • RECV: 들어오는 가장 높은 우선순위 메시지를 수집하거나, 메시지가 도착할 때까지 블록함
    • SEND: 메시지와 제어를 수신 태스크로 전송하고 호출자를 중지시킴. 호출자는 응답을 받을 때까지 대기상태로 전환됨
    • REPLY: 이전에 SEND를 사용한 태스크에 응답을 전달하여 계속 진행할 수 있도록 함

새롭고 흥미로운 실패 모드

  • IPC는 태스크 경계를 넘나들고 Hubris의 태스크는 별도로 컴파일되는 프로그램이기 때문에, 컴파일러가 함수 호출 시 가정하는 것과 같은 가정을 IPC에서 할 때는 주의해야 함
  • Hubris에서 IPC 서버 역할을 하는 모든 태스크는 다음과 같은 잠재적 오류를 처리해야 함:
    • 인터페이스와 부적절한 오퍼레이션 코드를 가진 메시지를 받는 경우
    • 기대한 메시지 유형 대신 해석할 수 없는 바이트 묶음을 받거나, 너무 짧거나 긴 메시지를 받는 경우
    • 필요한 메모리(쓰기 가능한 메모리 등)를 받지 못하는 경우

정상적이고 올바른 프로그램에서는 이러한 상황이 발생하지 않음

  • 정상적인 Hubris 프로그램에서는 위에서 언급한 상황들이 발생하지 않음
  • 태스크는 빌드 시스템의 구성에 의해 서로 연결되므로 서로 혼동하기 어려움
  • 클라이언트와 서버는 생성된 Rust 코드를 사용하므로 유형 시스템이 태스크 경계를 넘어 동작하는 것처럼 가정할 수 있음

커널은 어떠한 말도 안되는 상황도 허용하지 않음

  • Unix에서는 시스템 콜의 전제조건을 위반하면 호출자에게 오류 코드를 반환하지만, Hubris에서는 태스크가 즉시 파괴됨
  • Hubris 커널은 합성 오류(Synthetic Fault)라는 개념을 통해 커널 규칙을 위반하는 태스크에 오류를 전달함
  • Hubris에서는 하드웨어 또는 합성 오류가 발생하면 태스크가 즉시 중단되며 복구할 수 없음

서버도 어떠한 말도 안되는 상황을 허용하지 않음

  • REPLY_FAULT라는 13번째이자 가장 특이한 시스템 콜을 통해 서버가 클라이언트에게 오류를 전달할 수 있음
  • REPLY_FAULT는 REPLY와 유사하지만, 메시지를 전달하고 태스크를 실행 가능한 상태로 만드는 대신 오류를 전달하고 태스크를 중단시킴
  • REPLY_FAULT를 통해 필요 없는 IPC 오류 처리를 피할 수 있음. 정상 프로그램은 문제가 발생할 수 없는 것처럼 동작하고, 비정상 프로그램은 오류를 처리할 기회조차 갖지 못함
  • REPLY_FAULT는 액세스 제어 규칙과 같은 애플리케이션별 오류를 정의하고 구현하는 새로운 방법을 제공함

GN⁺의 의견

  • REPLY_FAULT는 서버가 클라이언트 측의 협조 없이도 클라이언트에서 크로스-프로세스 panic!을 발생시킬 수 있는 강력한 메커니즘임. 이를 통해 Hubris는 악의적인 프로그램에 대해 매우 적대적인 시스템이 됨
  • REPLY_FAULT의 단점은 퍼징 테스트가 매우 어려워진다는 것임. 랜덤한 IPC나 시스템 콜을 생성하는 카오스 엔지니어링 태스크는 거의 모든 동작에서 즉시 재설정됨
  • 그러나 정상적인 Hubris 태스크는 동적으로 IPC 메시지를 생성하지 않기 때문에 REPLY_FAULT의 존재 자체를 인식하지 못한 채 작동할 수 있음
  • REPLY_FAULT를 통해 서버는 클라이언트에게 무작위로 오류를 발생시킬 수 있지만, 이에 대한 평가는 아직 완전히 이루어지지 않음
  • Hubris의 공격적인 오류 처리는 개발 초기에 오류를 발견하는 데 도움이 되며, 오류 코드와 달리 오류를 무시할 수 없게 만듦
  • IPC 오류를 처리하는 보편적인 방법을 사용하면 정상적인 프로그램에도 불필요한 오버헤드가 발생할 수 있음. REPLY_FAULT는 이를 피하면서도 비정상적인 프로그램에 대해서는 엄격하게 대응할 수 있는 우아한 해결책으로 보임
Hacker News 의견

요약하면 다음과 같습니다:

  • REPLY_FAULT가 연쇄적으로 전파되는지 여부와 그로 인한 취약점에 대한 우려가 제기됨

    • A가 B를 기다리고, B가 C를 기다리는 상황에서 C가 REPLY_FAULT를 하면 A도 함께 종료되는지 확인 필요
    • 만약 그렇다면 전체 시스템이 취약해질 수 있음
    • SEND가 순환 구조라면 의도치 않게 자기 자신을 종료시킬 수도 있음
  • REPLY_FAULT는 접근 제어 등 애플리케이션 특화 오류를 정의하고 구현하는 방법을 제공함

    • 이는 시스템이 작고 밀접하며 대부분 시스템 설계자가 애플리케이션을 작성할 때 유용함
    • 그러나 제3자 코드와 연동할 때는 상대방이 언제든 프로세스를 즉시 종료시킬 수 있어 우려됨
    • 세상에는 관리자에게 시달리는 개발자들이 작성한 많은 열악한 드라이버와 백그라운드 프로세스가 존재함
  • 한 팀이 모든 코드를 작성하는 시스템에서는 의심스러운 클라이언트를 제거하는 것이 반복 속도를 높일 수 있음

  • Hubris는 서버가 클라이언트가 처리할 수 없는 효과를 수행하도록 하는 커널로 볼 수 있음

    • 이는 코드 재사용과 구성을 어렵게 만들지만 실행 모델을 단순화함
    • 정적 임베디드 시스템에서는 올바른 절충안이 될 수 있음
  • Hubris와 Humility는 깊이 몰두하고 싶은 기술이지만 시간과 의무의 한계로 어려움

  • HTTP에 대한 만우절 RFC로 "당신이 부끄러워해야 합니다"라는 의미의 HTTP 499 상태 코드를 제안함

    • 이는 "뭐야... 그런데 사실 괜찮네" 같은 맥락에 어울림
  • 아인슈타인의 명언 "가능한 한 단순하게, 그러나 지나치게 단순하지 않게"를 인용하며 Hubris의 설계가 후자를 위반했다고 지적함

    • 실제 세계의 혼란을 전혀 허용하지 않는 운영 환경에는 관심이 없음
  • Humility는 디버거로서 훌륭한 이름임

    • 많은 프로그래머들이 "좋은" 코드는 디버깅이 필요 없다고 가정하고 디버거 사용을 거부함
  • Linux에서는 소켓만으로 다른 프로그램을 직접 종료시킬 수는 없지만, root 권한으로 다른 프로세스를 종료시키거나 심지어 재부팅할 수 있음

    • 컨테이너에서는 root 권한이 일반적이라 이런 위험성이 존재함
  • "받아들일 때는 관대하고 내보낼 때는 엄격하라"는 기존 네트워크 시스템의 지혜와는 다소 상충됨

    • 그러나 API를 변경하면서 기존 프로그램의 호환성을 유지하려면 받아들일 때 관대해야 할 수밖에 없음