Ripgrep 15.2.0 musl 바이너리 대규모 검색 중 세그멘테이션 오류 발생

Ripgrep musl 바이너리 SIGSEGV 충돌 발생

x86_64-unknown-linux-musl 타겟으로 빌드된 Ripgrep 버전 15.2.0 바이너리는 높은 동시성으로 매우 큰 검색을 수행할 때 가끔 SIGSEGV (segmentation fault)와 함께 충돌이 발생합니다. 이 충돌은 opendir에 의해 시작된 calloc 호출 중에 발생하며, 구체적으로 musl의 mallocng 할당기 내의 힙 메타데이터에 관한 무결성 어설션 실패를 트리거합니다.

기술적 근본 원인 및 백트레이스

충돌은 ripgrep이 매우 큰 디렉토리 트리를 탐색할 때—특히 수백만 개의 파일을 포함하는 경우(예: 20GiB 데이터에 걸쳐 180만 개의 파일)—발생합니다. 오류는 mallocng/meta.h 내의 get_meta()에서 충돌이 발생하며, 이는 힙 메타데이터가 손상되었거나 일관성이 없음을 나타냅니다.

충돌로 이어지는 실행 경로는 다음과 같습니다:

  1. ignore::walk::Worker::run이 디렉토리 탐색을 시작합니다.
  2. 디렉토리 내용을 나열하기 위해 std::fs::read_dir이 호출됩니다.
  3. 이는 C 라이브러리 함수인 opendir 호출로 이어집니다.
  4. opendir은 디렉토리 스트림을 위한 메모리를 할당하기 위해 calloc을 호출합니다.
  5. mallocng (musl의 할당기)가 힙 메타데이터에서 무결성 위반을 감지하고 세그멘테이션 오류를 트리거합니다.

재현 요구 사항

버그를 재현하려면 규모와 동시성에 관련된 특정 조건이 필요합니다:

  • 대상 바이너리: x86_64-unknown-linux-musl용으로 빌드된 바이너리 (예: 공식 15.2.0 릴리스 바이너리).
  • 규모: 방대한한 수의 파일을 포함하는 디렉토리 트리. 재현 스크립트(generate_repro_tree.py)를 사용하여 약 180만 개의 파일과 20GiB의 데이터를 가진 트리를 생성했습니다.
  • 동시성: 높은 CPU 코어 수 (예: 24 코어) 및 높은 동시성 수준.
  • 환경: 이 문제는 OpenSUSE Tumbleweed Linux x86_64에서 관찰되었습니다.

커뮤니티 통찰 및 분석

이 문제에 관한 논의는 근본 원인이 ripgrep 자체를 넘어 musl libc와 Linux 커널 사이의 더 깊은 상호작용을 가리키고 있음을 강조합니다.

커널 수준의 버그

증거에 따르면 이 문제는 실제로 커널 버그의 발현입니다. 커뮤니티 구성원들은 커널 패치와, 충돌을 ripgrep의 Rust 코드 내의 논리적 오류가 아닌 커널 수준의 메모리 관리와 연결하는 상세한 분석을 참조했습니다.

할당기 성능 및 경합(Contention)

일부 사용자들은 musl의 mallocng가 높은 동시성의 멀티스레딩 중에 경합을 처리하는 데 어려움을 겪는 것으로 알려져 있다고 언급했습니다. 한 기여자는 기본 musl 할당기를 성능이 더 좋은 mimalloc과 같은 대 더할 만한 대안으로 교체하는 것이 경합을 크게 줄이고 고성능 애플리케이션에서 이러한 유형의 오류를를 피할 수 있는 잠재적 방법이라고 제안했습니다:

"mallocng는 멀티스레딩 중의 경합을 처리하는 데 서툽니다... mimalloc로 전환하면 성능이 향상되었습니다 20배... 애초에 이런 식으로 나타나서는 안 되었습니다."

파일시스템 영향

운영 관점에서는, 거대한 클러스터 파일시스템에서 높은 동시성의 검색을 실행하는 것은 파일시스템의 메타데이터 메커니즘에 해로운 영향을 줄 수 있습니다. 한 사용자는 이러한 작은 I/O 패턴이 메타데이터 서버에 극심한 압력을 가줍니다, 이는 고대역폭 파일시스템을 마동하게 만들 수 있다고 경고했습니다.

Sources