Making Rust Workers reliable: panic and abort recovery in wasm‑bindgen (새 탭에서 열림)

Cloudflare Workers 환경에서 Rust로 작성된 WebAssembly(Wasm)는 예기치 못한 패닉(Panic)이나 중단(Abort)이 발생할 경우 런타임이 정의되지 않은 상태로 남아 동일한 인스턴스의 다른 요청까지 실패하게 만드는 '샌드박스 오염' 문제를 안고 있었습니다. 이를 해결하기 위해 Cloudflare는 wasm-bindgen 메인 프로젝트와 협력하여 Wasm 예외 처리 기능을 활용한 panic=unwind 지원과 중단 복구 메커니즘을 도입했습니다. 결과적으로 단일 요청의 실패가 전체 서비스 중단으로 이어지는 것을 방지하고, 상태 유지가 필요한 애플리케이션에서도 안정적인 오류 복구가 가능해졌습니다.

초기 대응 및 완화 전략

본격적인 기능 개선에 앞서, Cloudflare는 운영 환경에서의 피해를 최소화하기 위해 사용자 정의 패닉 핸들러와 JavaScript 프록시를 활용한 초기 완화책을 적용했습니다.

  • 패닉 핸들러 도입: Rust 내부에 상태를 추적하는 패닉 핸들러를 설치하여 실패가 발생하면 전체 애플리케이션을 다시 초기화하도록 설정했습니다.
  • Proxy 기반 간접 참조: JavaScript 영역에서 Rust 호출 경계를 Proxy로 감싸 모든 진입점을 캡슐화하고, 실패 시 안전하게 Wasm 모듈을 재로드했습니다.
  • 한계점: 이 방식은 서비스 가용성은 높였으나, 전체 애플리케이션을 재시작해야 하므로 메모리에 상태를 보관하는 Durable Objects 같은 서비스에서는 데이터 손실이 발생하는 단점이 있었습니다.

Wasm 예외 처리를 통한 panic=unwind 구현

상태를 보존하면서 오류를 복구하기 위해 Wasm의 최신 표준인 예외 처리(Exception Handling) 제안을 활용하여 Rust의 패닉 언와인딩(Unwinding)을 구현했습니다.

  • 컴파일 옵션 변경: RUSTFLAGS='-Cpanic=unwind'-Zbuild-std를 사용하여 표준 라이브러리가 언와인딩을 지원하도록 다시 빌드했습니다.
  • wasm-bindgen 툴체인 업데이트: Wasm 파서인 Walrus가 try, catch, rethrow 명령어를 인식하도록 수정하고, JavaScript와 Rust 경계에서 예외가 올바르게 전달되도록 개선했습니다.
  • Boundary 처리: Rust에서 발생한 패닉이 JavaScript의 PanicError로 변환되도록 했으며, extern "C-unwind"를 통해 함수 호출 경계를 넘나드는 언와인딩을 허용했습니다.
  • 클로저 안전성: MaybeUnwindSafe 트레잇을 도입하여 언와인딩 시 안전하지 않은 참조를 캡처하는 클로저를 체크하고, 필요한 경우 패닉 시 즉시 중단되는 Closure::new_aborting 변형을 제공합니다.

중단(Abort) 복구 및 포이즌 필(Poison Pill) 메커니즘

메모리 부족(OOM)과 같이 언와인딩이 불가능한 '중단' 상황에서는 메모리 상태가 오염될 가능성이 높기 때문에, 해당 인스턴스의 재실행을 원천 봉쇄하는 전략을 사용합니다.

  • 포이즌 필 플래그: wasm-bindgen은 이제 모든 Wasm 모듈에 내부 상태 플래그를 주입합니다. 만약 Rust 코드에서 중단이 발생하면 이 플래그가 즉시 설정됩니다.
  • 재실행 방지: 이후 JavaScript에서 해당 Wasm 인스턴스의 어떤 함수라도 호출하려고 하면, Rust 코드를 실행하기 전에 플래그를 먼저 확인하여 즉시 JavaScript 에러를 발생시킵니다.
  • 안전성 보장: 이를 통해 오염된 메모리 상태에서 코드가 다시 실행되어 발생할 수 있는 보안 취약점이나 예측 불가능한 동작을 완전히 차단합니다.

Wasm 기반의 Rust 서비스를 운영한다면 최신 버전의 wasm-bindgenworkers 라이브러리를 사용하고, 특히 Durable Objects와 같이 상태 보존이 중요한 경우 panic=unwind 설정을 활성화할 것을 권장합니다. 이는 단순한 안정성 향상을 넘어, Wasm이 네이티브 환경과 동등한 수준의 오류 복구 능력을 갖추게 되었음을 의미합니다.