해결책
브라우저는 낯선 사람이 만든 미디어 파일을 파싱하는 코드를 신뢰하며, 공격자들은 그러한 포맷들을 디코더의 메모리 버그에 대항해 반복적으로 무기화해 왔다. Mozilla는 C++의 안전한 대안으로 구상된 언어인 Rust를 거의 7년 동안 후원해 왔지만, 2016년 중반까지 Firefox에 출시된 것은 없었다. 안전 우선 언어는 거대하고 성능이 중요한 C++ 브라우저 안에서 스스로를 입증해야 했다.
사용자를 보호하는 뻔한 방법들은 모두 부족했다. C++ 파서를 패치하는 것은 새로운 취약점마다 영원히 방어해야 한다는 뜻이었고, 샌드박싱은 버그 클래스를 제거하지 않은 채 피해 범위만 줄였으며, 브라우저 전체를 Rust로 다시 작성하는 것은 그 과정에서 제품을 망가뜨릴 다년간의 도박이었다. 대신 Mozilla의 미디어 재생 팀은 경계가 명확하고 보안에 중요한 한 조각, 즉 신뢰할 수 없는 웹 미디어에서 실행되는 mp4 메타데이터 파서를 선택했다.
Ralph Giles와 Matthew Gregan은 그 파서를 순수 Rust로 대체 구현하고, 주변의 C++ 빌드가 그대로 유지되도록 C API를 통해 노출했으며, 대체된 코드와 동일한 결과를 내는지 검증했다. 그런 다음 Firefox 48이 이를 모든 데스크톱 플랫폼에 탑재하여 처음으로 수억 명의 사용자 앞에 프로덕션 Rust를 내놓았고, Android 지원은 다음으로 계획되었다.
이 시범 사업이 성공한 것은 이념을 증거로 바꾸었기 때문이다. Rust의 컴파일 시점 규칙은 방어 계층을 하나 더 추가하는 대신 해당 컴포넌트의 메모리 버그 클래스를 제거했고, Firefox 텔레메트리는 새 코드의 10억 회 이상 사용에서 문제를 전혀 기록하지 않았다. Mozilla는 이를 끝이 아닌 첫걸음이라고 불렀다. 즉, 앞으로의 더 큰 이전을 위한 입증된 템플릿, 곧 가장 위험한 경계 컴포넌트를 교체하고, 동등성을 입증하고, 출시하고, 측정하는 방식이다.
달성한 것
신뢰할 수 없는 데이터를 처리하는 파서에서 이전을 시작하라
성공한 이유
미디어 파싱은 신뢰할 수 없는 바이트가 네이티브 코드와 만나는 지점이므로, 시범 사업은 코드 라인당 남은 가장 큰 위험을 겨냥했다
자기 완결적인 파서는 명확하게 테스트 가능한 경계를 가진다. C++ 컴포넌트와 동일한 출력인지 확인할 수 있었다
컴파일 시점 메모리 안전성은 방어 계층을 하나 더 추가하는 대신 버그 클래스 전체를 제거했다
10억 회 사용 동안 텔레메트리 문제가 전혀 없었던 것이 언어에 대한 도박을 측정 가능한 프로덕션 증거로 바꾸었다
Rust를 C API를 통해 노출함으로써 파서는 나머지 C++ 빌드를 방해하지 않고 출시될 수 있었다
새 기술을 레거시 코드베이스에 도입하는 것은 재작성이 아니라 이식이다. 테스트 가능한 경계를 가진 가장 위험한 컴포넌트를 선택하고, 출시하고, 프로덕션 데이터로 더 많은 도입의 근거를 마련하라
그 후
Firefox 48은 첫 프로덕션 Rust를 모든 데스크톱 사용자에게 제공했고, Mozilla는 이 이정표를 끝이 아닌 시작 수로 취급했다. Oxidation 위키는 어떤 Rust 기능이 각 플랫폼에 언제 적용될지 정리했고, Bugzilla 1284816은 Rust를 Firefox 빌드의 필수 요소로 만드는 작업을 추적했다. 출시 과정에서 발견된 저메모리 충돌은 충돌하는 대신 오류를 반환하는 실패 가능한 스레드 빌더 API로 전환하여 Firefox 48에서 수정되었다. 텔레메트리로 입증된 경계가 명확한 메모리 안전 대체물이라는 같은 경로가 이후에 등장한 Rust 컴포넌트들의 청사진이 되었다.
근거 확인하기
출처
- Shipping Rust in Firefox Mozilla Hacks · hacks.mozilla.org ·
- mozilla/mp4parse-rust — Parser for ISO Base Media Format aka video/mp4 written in Rust GitHub · github.com