저자: 코리 미니야드
핵심 결론
이전 글에서 저는 안정성과 제가 작업했던 몇 가지 커널 버그에 대해 이야기했습니다. 그런데 이것들은 정확히 무엇을 의미할까요?
참고로, 이러한 버그들은 고객이 재현 가능한 환경을 제공하지 못했습니다. 오직 완벽한 실험실 환경에서만 발생했고, 심지어 고객사 현장에서도 발생했습니다. 대부분의 고객은 품질과 신뢰성을 매우 중요하게 생각하며, 일반적으로 커널 코어덤프를 설정하고 크래시 정보를 기록하는 메커니즘을 갖추고 있으며, 철저한 테스트를 수행합니다. 즉, 시스템을 극한까지 몰아붙이고 오류 발생 시 필요한 정보를 추출할 수 있습니다. 저희 또한 테스트를 진행하며, 커널은 다양한 출처에서 수많은 테스트를 거칩니다. 그럼에도 불구하고 버그는 계속해서 발생합니다.
이 버그 예시들은 설명적인 목적이지만, 제가 실제로 작업했던 버그들의 범위와 잘 맞아떨어집니다. 따라서:
- 고객이 코드를 추가하면 거의 확실히 버그가 포함될 것입니다. 해당 코드는 주류 커널 프로세스를 거치지 않았으며, 상당한 조치가 취해지지 않는 한 주류 커널 코드보다 품질이 떨어질 가능성이 매우 높습니다.
- 펌웨어 또한 위험 요소 중 하나입니다. 일부 시스템은 부팅 후 펌웨어 없이도 작동할 수 있지만, 펌웨어는 시스템 부팅을 담당하기 때문에 여전히 중요합니다. 또 다른 시스템은 펌웨어가 항상 실행됩니다. 펌웨어는 소프트웨어만큼이나 중요하며, 종종 클로즈드 소스로 제공됩니다. 저는 수많은 펌웨어 코드를 접해봤는데, 솔직히 말해서 신뢰감을 주지는 못합니다.
- 커널에는 경쟁 조건(race condition)이 만연해 있습니다. 이러한 버그는 끊임없이 발견됩니다. 이러한 종류의 버그는 발견하기가 매우 어렵고, 현재의 분석 도구로는 커널 규모를 제대로 분석할 수 없습니다. 발생 빈도도 낮고, 발생하더라도 근본 원인을 추적하기가 어렵습니다. 버그가 후속 패치에서 수정된 사례를 몇 번 본 적이 있는데, 그런 패치들은 버그를 수정한다는 사실조차 인지하지 못한 채 품질 개선을 위한 패치였습니다.
- 최적화 컴파일러와 슈퍼스칼라 시스템에서 메모리 연산 순서와 관련된 경쟁 조건 유형의 버그가 존재합니다. (궁금한 점이 있으면 메모리 배리어를 검색해 보세요.) 적절한 배리어를 구현하는 것은 매우 어렵습니다. 다행히 대부분의 Linux 코드는 순서 관리를 위해 뮤텍스와 락을 사용하지만, 이러한 유형의 버그는 여전히 커널에 숨어 있을 가능성이 높으며 경쟁 조건보다 훨씬 더 어렵습니다. 또한 RCU(Risk Control Unit)도 있는데, 이는 커널에서 확장성을 확보하는 데 필수적이기 때문에 매우 어렵고 많이 사용됩니다. (궁금한 점이 있으면 Linux RCU를 검색해 보세요. RCU는 처음에는 이해하기 어려울 수 있습니다.)
고객이 추가한 코드는 품질 관리 프로세스를 통해 관리할 수 있습니다.
펌웨어는 다루기 어렵지만, 부팅 후 아무런 작업도 수행하지 않는 매우 간소화된 부트로더는 아마도 관리 가능할 것입니다. 그러나 대부분의 펌웨어는 시스템 실행에 거대한 블랙박스를 도입합니다. 특히 ACPI와 EFI가 운영체제의 제어 없이 시스템 작동을 선점할 수 있게 되면서 이러한 문제는 더욱 심각해졌습니다. 실시간 리눅스 그룹은 일부 시스템에서 펌웨어가 지연의 원인이라고 지적했으며, 어떤 시스템에서는 펌웨어가 운영체제 작동과 비동기적으로 실행될 수 있습니다. 안전을 위해 펌웨어는 시스템의 다른 부분과 마찬가지로 엄격한 요구 사항을 충족해야 합니다. EFI와 ACPI는 크기가 매우 커서 커널과 맞먹을 정도입니다.
커널 경쟁 조건은 현재 기술로는 제대로 관리하기 어렵습니다. 정확한 수치를 알려드리고 싶지만, 그런 자료는 구하기가 매우 어렵습니다. 제가 말씀드릴 수 있는 것은 리눅스 커널을 유지 관리해 온 경험을 바탕으로 한 답변뿐입니다.
제 생각에 리눅스 커널의 오류 확률을 10⁻⁶까지 낮추려면 제가 이전에 언급했던 경쟁 조건과 사용 후 해제 시나리오를 찾아낼 수 있는 도구가 필요할 것입니다. 그런 도구가 개발되기 전까지는 리눅스 커널 자체만으로는 안전에 중요한 시스템에 필요한 오류 확률을 충족할 수 없다고 생각합니다. 그런 도구가 있다고 해도 10⁻⁷까지는 달성하기 어려워 보입니다. 선점형 시스템에서 10⁻⁸까지는 불가능해 보입니다. 너무 복잡하기 때문입니다. 11,500년에 한 번꼴로 오류가 발생하는 셈인데, 어떻게 측정할 수 있을지조차 모르겠습니다. 아마도 충분히 큰 규모로 확장 가능한 형식 분석 기법이 개발된다면 가능할지도 모르지만, 아직 갈 길이 멉니다.
이건 커널에 대한 이야기일 뿐입니다. 커널 엔지니어들이 "유저랜트"라고 부르는 부분도 고려해야 합니다. 안전에 중요한 기능들이 그곳에서 실행된다면, 사용자 공간에 필요한 라이브러리와 도구들도 중요합니다. 사용자 공간 코드는 커널 코드보다 훨씬 많고, 많은 경우 품질에 대한 관심도 떨어집니다. 그리고 펌웨어와 하드웨어도 있죠. 네, 상황이 꽤 심각해 보입니다.
그렇다면 Xen은 어떨까요?
Xen을 살펴보는 데 시간을 좀 할애했습니다. 커널보다 코드 크기가 작고 복잡성도 다소 낮습니다. 잘 작성되고 잘 설계된 것처럼 보입니다. 하지만 여전히 똑같은 문제점들이 남아 있습니다. 여전히 멀티스레드 선점형 시스템이기 때문에 경쟁 조건(race condition)이 많을 것이 분명합니다. 게다가 Xen이 관리하지 않지만 필요한 모든 장치들을 관리할 무언가가 필요합니다. 리눅스 커널의 안전성을 인증하는 것이 태평양을 헤엄쳐 건너는 것과 같습니다. 불가능한 일이죠. Xen을 인증하는 것은 대서양을 헤엄쳐 건너는 것과 같습니다. 크기는 훨씬 작지만 여전히 불가능한 일입니다.
어떤 하이퍼바이저를 사용하든 똑같은 문제가 발생할 겁니다. 하는 일도 똑같으니까요.
영국 해협을 헤엄쳐 건너는 것과 같은 무언가가 필요해요. 물론 힘들겠지만, 불가능한 건 아니잖아요.
그렇다면 우리는 희망을 버려야 할까요?
아닐 수도 있어요. 다음 글에서 몇 가지 선택 사항에 대해 이야기해 볼게요.
Please reach out to discuss your particular scenario today.