# C stdlib의 비스레드 안전성과 안전한 Rust의 실패

> Clean Markdown view of GeekNews topic #18880. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=18880](https://news.hada.io/topic?id=18880)
- GeekNews Markdown: [https://news.hada.io/topic/18880.md](https://news.hada.io/topic/18880.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-01-24T10:00:05+09:00
- Updated: 2025-01-24T10:00:05+09:00
- Original source: [edgedb.com](https://www.edgedb.com/blog/c-stdlib-isn-t-threadsafe-and-even-safe-rust-didn-t-save-us)
- Points: 5
- Comments: 4

## Summary

EdgeDB의 네트워크 I/O 코드를 Rust로 포팅하는 과정에서 ARM64 CI 러너에서 테스트가 간헐적으로 실패하는 문제가 발생했으며, 이는 setenv와 getenv의 상호작용에서 발생한 충돌 때문임이 밝혀졌습니다. 이 문제는 openssl-probe가 SSL 관련 환경 변수를 설정하면서 발생했으며, 여러 조건이 맞아떨어져야만 ARM64 Linux에서 충돌이 발생했습니다. 해결책으로는 reqwest의 rust-native-tls/openssl 백엔드를 rustls로 전환하고, Rust 프로젝트와 glibc 프로젝트에서 관련 함수의 안전성을 개선하는 방안이 제시되었습니다.

## Topic Body

- # 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의 스레드 안전성을 향상시킴.

## Comments



### Comment 33839

- Author: carnoxen
- Created: 2025-01-24T23:28:14+09:00
- Points: 2

[Setenv는 쓰레드세이프 하지 않으며, C는 이를 수정하고 싶지 않음](https://news.hada.io/topic?id=11992)  
  
Setenv 함수가 또 말썽이군요.

### Comment 33808

- Author: y15un
- Created: 2025-01-24T11:03:32+09:00
- Points: 5

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

### Comment 33837

- Author: halfenif
- Created: 2025-01-24T20:21:16+09:00
- Points: 1
- Parent comment: 33808
- Depth: 1

확실하게 이해했습니다.

### Comment 33804

- Author: neo
- Created: 2025-01-24T10:00:05+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=42796058) 
- Rust의 다음 에디션에서 환경 설정자를 안전하지 않게 만들 예정임. 이는 충돌을 일으키는 크레이트에 영향을 줄 수 있음
  - Rust 표준 라이브러리에서 `set_var`와 `remove_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의 주소가 변경되었기 때문임
