5P by neo 1달전 | ★ favorite | 댓글 4개
  • ARM64에서만 발생한 충돌

    • EdgeDB의 네트워크 I/O 코드를 Python에서 Rust로 포팅하는 과정에서 ARM64 CI 러너에서 테스트가 간헐적으로 실패하는 문제가 발생함.
    • 처음에는 데드락으로 보였으나, 실제로는 프로세스가 충돌하여 테스트 러너가 이를 감지하지 못한 것임.
  • 초기 이론

    • ARM64에서만 문제가 발생하는 이유를 이해하기 위해 메모리 모델의 차이를 고려함.
    • Intel의 메모리 모델은 엄격한 반면, ARM은 더 약한 메모리 모델을 가짐.
  • CI 머신에서의 디버깅

    • AWS에서 ARM64 러너에 직접 연결하여 문제를 조사함.
    • 프로세스가 충돌하여 코어 덤프를 남겼고, 이를 조사하여 문제의 원인을 파악함.
  • 실제 원인: setenv와 getenv

    • setenv는 멀티스레드 환경에서 안전하지 않으며, getenv와의 상호작용에서 충돌을 일으킬 수 있음.
    • 환경 변수의 재할당이 문제의 원인으로 밝혀짐.
  • openssl_probe와의 연결

    • openssl-probe가 SSL_CERT_FILE과 SSL_CERT_DIR 환경 변수를 설정하면서 문제가 발생함.
    • Rust의 rust-native-tls가 이러한 환경 변수를 설정하는 과정에서 충돌이 발생함.
  • ARM64 Linux에서만 발생한 이유

    • 여러 조건이 맞아떨어져야만 충돌이 발생하며, 환경 변수의 수와 I/O 실패 등이 그 조건에 해당함.
  • 해결책

    • reqwest의 rust-native-tls/openssl 백엔드에서 rustls로 전환하기로 결정함.
    • Rust 프로젝트는 환경 설정 함수를 비안전하게 만들 계획이며, glibc 프로젝트는 getenv의 스레드 안전성을 향상시킴.

저는 제목을 'C stdlib의 스레드 불안전성은 그 안전하다는 Rust도 구제하지 못한다'라고 적겠습니다. :)

확실하게 이해했습니다.

Hacker News 의견
  • Rust의 다음 에디션에서 환경 설정자를 안전하지 않게 만들 예정임. 이는 충돌을 일으키는 크레이트에 영향을 줄 수 있음

    • Rust 표준 라이브러리에서 set_varremove_var는 2024년 에디션에서 unsafe {} 블록을 사용해야 함
    • 현재 문서에는 안전성 문제를 언급하고 있지만, 원래 이러한 함수들을 안전하게 만든 것은 실수였음
  • glibc에 대한 패치가 getenv를 더 안전하게 만들었지만, C는 여전히 환경에 직접 접근을 허용하여 완전히 안전하지는 않음

    • C 표준 라이브러리 유지보수자들이 setenv를 멀티스레드 안전하게 만들기를 꺼려하지만, 최소한 새로운 스레드 안전 API가 정의되어야 함
    • Musl의 유지보수자가 이 문제를 해결할 수 없다고 확신하지 않음
  • 리눅스에서 환경 관련 버그를 겪는 것은 일종의 통과의례처럼 여겨짐

    • Linus와 커널은 POSIX 버그를 해결하는 데 실용적이지만, glibc는 여전히 뒤처져 있음
    • getenv_r()를 제공하고 setenv()와 동기화하며 컴파일/링크 시 경고를 제공하는 것이 문제 해결에 도움이 되었을 것임
  • 환경 변수를 사용한 설정은 "12-factor app" 운동의 일부였지만, 이는 어리석은 방법이라고 생각함

    • 환경 변수 대신 YAML과 같은 설정 파일을 사용하는 것이 더 나은 방법이라고 생각함
  • Amazon AWS에서 실행되는 CI 머신은 실제 루트 사용자를 제공하여 장점이 있음

    • 클라우드와 컨테이너 없이 로컬에서 코드 빌드 및 디버그 능력을 잃은 것 같음
  • 비직관적인 버그를 파헤치는 훌륭한 기사임

    • 이러한 상세한 문제 해결 보고서는 직접 해보는 것과 가장 가까운 경험을 제공함
  • env::set_var는 이제 안전하지 않음

    • 단일 스레드 프로그램에서는 안전하게 호출할 수 있음
    • Windows에서는 단일 및 멀티 스레드 프로그램 모두에서 항상 안전함
    • 다른 운영 체제의 멀티 스레드 프로그램에서는 set_var 또는 remove_var를 사용하지 않는 것이 유일한 안전한 선택임
  • setproctitle이 특정 코드베이스에서 작동하지 않았던 경험을 상기시킴

    • numpy를 임포트한 후 setproctitle이 작동하지 않았고, 이는 numpy 초기화 시 getenv 또는 setenv 호출로 인해 **environ의 주소가 변경되었기 때문임