# Ripgrep musl 바이너리가 초대형 검색 중 간헐적으로 세그멘테이션 오류를 일으킴

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

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=32051](https://news.hada.io/topic?id=32051)
- GeekNews Markdown: [https://news.hada.io/topic/32051.md](https://news.hada.io/topic/32051.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-08-02T10:36:14+09:00
- Updated: 2026-08-02T10:36:14+09:00
- Original source: [github.com/BurntSushi](https://github.com/BurntSushi/ripgrep/issues/3494)
- Points: 1
- Comments: 1

## Topic Body

- Ripgrep 15.2.0의 **x86_64-unknown-linux-musl** 바이너리가 대규모 파일 트리를 높은 동시성으로 검색할 때 간헐적으로 `SIGSEGV`와 함께 종료됨
- 충돌은 `opendir`가 호출한 `calloc` 내부에서 발생하며, **musl mallocng**의 힙 메타데이터 무결성 검사 지점이 스택 추적의 최상단에 나타남
- 재현 환경은 약 **20GiB·180만 개 파일**로 구성된 트리이며, 존재하지 않는 문자열을 `rg`로 반복 검색함
- 24코어 시스템에서 검색 트리가 커널 블록 캐시에 들어갈 만큼 RAM을 확보하면 일반적으로 **약 1분** 안에 문제가 발생함
- OpenAI Codex에 포함된 `rg`뿐 아니라 공식 릴리스와 바이트 단위로 동일한 바이너리에서도 독립적으로 재현돼 **Codex 의존성과 무관한 문제**로 확인됨

---

### 발생 환경
- 사용 버전은 **ripgrep 15.2.0** rev `e89fff8`이며 `+pcre2` 기능을 포함함
  - 컴파일 시 SIMD: `+SSE2,-SSSE3,-AVX2`
  - 실행 시 SIMD: `+SSE2,+SSSE3,+AVX2`
  - PCRE2 10.45와 JIT를 사용할 수 있음
- 운영체제는 **OpenSUSE Tumbleweed Linux x86_64**임
- 최초 발견된 OpenAI Codex 번들 `rg`는 [공식 x86_64-unknown-linux-musl 릴리스](https://github.com/BurntSushi/ripgrep/releases/download/15.2.0/ripgrep-15.2.0-x86_64-unknown-linux-musl.tar.gz)와 바이트 단위로 동일함
- Codex와 별개로 공식 바이너리에서도 재현됐으며, 분석용 바이너리는 다음 명령으로 디버그 심볼을 포함해 빌드함
  - `CROSS_CONTAINER_ENGINE=podman CARGO_PROFILE_RELEASE_DEBUG=true ~/.cargo/bin/cross build --release --target x86_64-unknown-linux-musl`

### 재현 절차
- [generate_repro_tree.py](https://github.com/user-attachments/files/30390612/generate_repro_tree.py)는 원래 문제가 발생한 저장소의 통계를 모방한 무작위 파일 트리를 생성함
  - 이 프로그램은 LLM으로 작성됨
  - 생성 결과는 약 **20GiB**, 180만 개 파일 규모임
- 생성된 트리의 루트에서 존재하지 않는 임의 문자열을 반복 검색함
  - `while true; do rg tnoheueunotshisnthukoethnsueothnsiuothonesuioseuinth; done`
- 충분히 큰 검색 트리가 재현에 필수적인 것으로 관찰됨
- **24코어 시스템**에서 트리 전체가 커널 블록 캐시에 들어갈 만큼 여유 RAM이 있으면 일반적으로 약 1분 후 충돌함

### 충돌 지점
- 실제 결과는 **코어 덤프를 남기는 `SIGSEGV`** 임
- 스택 추적 최상단은 musl mallocng의 `get_meta`이며, 힙 메타데이터 무결성 검사 지점에서 충돌함
- 호출 흐름은 `opendir`가 `calloc`을 호출하고, Rust 표준 라이브러리의 디렉터리 순회와 ripgrep `ignore::walk` 작업자로 이어짐
  - `get_meta` → `__malloc_allzerop` → `calloc` → `opendir`
  - `std::fs::read_dir` → `ignore::walk::Work::read_dir` → `ignore::walk::Worker::run`
- 분석 자료로 [코어 덤프](https://github.com/user-attachments/files/30390841/core.gz)와 [해당 rg 바이너리](https://github.com/user-attachments/files/30390840/rg.gz)가 첨부됨

### 기대 동작과 현재 상태
- 기대 동작은 대규모·고동시성 검색에서도 **세그멘테이션 오류 없이 실행**되는 것임
- 제공된 내용에는 원인 확정, 수정안, 검토 결과 또는 최종 해결 여부가 포함돼 있지 않음

## Comments



### Comment 62730

- Author: neo
- Created: 2026-08-02T10:36:15+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=49133889) 
- 커널 패치에 재미있는 대목이 있음: [https://lore.kernel.org/all/CALCETrXbj__SFQMzPZhES5y6-sh4np-...](https://lore.kernel.org/all/CALCETrXbj__SFQMzPZhES5y6-sh4np-ZHY5T_=4QY5+Fn8BM4A@mail.gmail.com/)  
  > ripgrep에서 재미있는 버그 보고서와, 성실하지만 상당히 형편없는 AI 생성 분석을 봤다  
  [https://github.com/dfoxfranke/ripgrep-3494-analysis](https://github.com/dfoxfranke/ripgrep-3494-analysis)를 가리키는 말인데, 나도 **사람이 썼다고 보기엔 지나치게 길다**고 생각했음. 게다가 이 스레드는 바로 오늘 올라온 것 같음
  - 읽기 고역이었고, 그 장황한 글 어디에서도 lore.kernel의 실제 사람이 찾아낸 것과 같은 코드나 영역을 짚은 부분을 못 찾겠음. Claude를 더 잘 이해하는 사람에게 묻고 싶은데, 실제 원인을 식별한 대목이 있기는 한가?
  - 2000년 무렵까지 공공장소에서 휴대전화를 쓰는 사람은 잘난 체하는 재수 없는 사람처럼 취급됐고, 지금 AI도 그와 비슷한 **불쾌한 거부 단계**를 지나고 있음  
    2년 전이었다면 이런 분석은 공동체에 시간을 후하게 기부한 결과로 여겨졌겠지만, 이제는 출처와 토큰 비용이 고작 **0.06달러**라는 걸 아니 읽으려 하지 않음. 앞으로 벌어질 일을 예고한다는 점도 한몫함  
    몇 년 뒤에는 사람이 직접 버그를 파헤치는 일이 최후의 수단이 되고, 다른 AI 에이전트가 보고서를 읽어 수정 사항을 검증할 것임. 컴파일러가 생성한 어셈블리와 공공장소의 휴대전화 사용자를 무시하듯, 비웃는 단계에서 무관심한 단계로 넘어갈 듯함

- musl의 기본 할당자를 편리하다는 이유로 그대로 쓰는 건 이해하지만, **속도 자체가 목적인 애플리케이션**에서 더 빠른 할당자로 바꾸지 않은 건 이상함  
  mallocng는 다중 스레드 경합에 취약함. 평소 입출력 병목이던 애플리케이션도 musl로 빌드하면 스레드 8개만으로 `malloc` 병목이 생겼고, mimalloc으로 바꾸자 성능이 **20배 향상**되어 glibc 기본 구성에 매우 가까워졌으며 glibc+mimalloc보다는 조금 느렸음  
  실제로 흥미로운 문제가 있는 건 맞지만 애초에 이런 식으로 드러나서는 안 됐음
  - ripgrep은 64비트 musl로 빌드할 때 실제로 **jemalloc을 전역 할당자**로 지정함: [https://github.com/BurntSushi/ripgrep/blob/435f59fc4b43af3ab...](https://github.com/BurntSushi/ripgrep/blob/435f59fc4b43af3ab32f34d53fa34978f393fe52/crates/core/main.rs#L20-L41)
  - 세그멘테이션 오류 스택을 보면 할당은 musl libc의 `opendir`에서 발생함. Rust가 사용하는 할당자 교체 방식은 프로세스 전체의 할당자를 바꾸는 게 아니라 **Rust 코드가 호출하는 할당자만 교체**함  
    다만 전역 잠금을 사용하는 할당자를 거쳐야 한다면 ripgrep이 libc의 `opendir` 사용을 피하는 편이 나아 보임
  - 이건 **커널 버그**임. libc 할당자들이 별 이유 없이 형편없다는 데는 동의하지만, 이 문제는 mimalloc이나 glibc를 비롯한 다른 애플리케이션 코드에서도 비슷한 확률로 발생할 수 있어 보임
  - 대부분의 프로그램은 **할당을 재사용**해 속도를 높임. 아예 할당하지 않는다면 빠른 할당자도 필요 없고, ripgrep의 작업 자체가 빈번한 할당을 본질적으로 요구하지도 않음
  - 오히려 musl의 mallocng가 제공하는 **강화 기능** 덕분에 커널 버그를 발견할 수 있었음. 그렇지 않았다면 수개월 동안 조용히 메모리를 손상시키며 눈에 띄지 않았을 수도 있음

- HPC 클러스터에서 대규모 클러스터 파일 시스템을 상대로 ripgrep을 실행한다면 즉시 멈추고 작업 흐름을 다시 설계해야 함. 이런 작업은 대량의 작은 입출력을 발생시키는데, 이는 **대규모 클러스터 파일 시스템의 아킬레스건**임  
  클러스터의 고대역폭 메모리 계층에서 처리할 일을 파일 시스템의 메타데이터 계층으로 떠넘기게 되며, 사용자 몇 명만 동시에 실행해도 고대역폭 파일 시스템 전체가 마비될 수 있음
  - 이건 HPC 클러스터가 아니라 내 워크스테이션에서 사용하는 **btrfs**일 뿐임
  - 최근 GitHub가 불안정해진 근본 원인도 비슷한 게 아닌지 궁금했음. AI 사용으로 증폭된 수십억 개의 작은 파일 작업이 갑자기 발생하고 있으며, 객체 그래프는 본질적으로 파편화돼 있어 페이지 하나를 미리 가져온 뒤 일반적인 Git 작업이 그 페이지 안의 객체만 건드리도록 만들기도 어려움  
    [https://isolveproblems.substack.com/p/how-microsoft-vaporize...](https://isolveproblems.substack.com/p/how-microsoft-vaporized-a-trillion)의 내용이 조금이라도 맞는다면, Azure의 파일 시스템 추상화에서 최적화되지 않은 경로 하나만 있어도 사용량 급증이 **막대한 장애 반경**으로 번질 수 있음

- 커널 버그 분석을 직접 연결하는 편이 더 나을 수 있음: [https://github.com/dfoxfranke/ripgrep-3494-analysis](https://github.com/dfoxfranke/ripgrep-3494-analysis)
  - 읽어보려 했지만 첫 번째 `Headline` 문단에서 포기했음  
    “새로 페이지 폴트가 난 익명 페이지에 스레드가 저장한 값이 약 10개 명령어 뒤 같은 스레드의 재읽기에서 사라지고, 함수 실행 도중 페이지의 backing이 교체된다”, “폴트 순간 pagemap을 읽으면 backing이 커널의 zero page다”, “메커니즘이 VMA별 잠금의 익명 폴트 빠른 경로와 동시 `munmap`의 TLB shootdown 사이 상호작용으로 localize된다” 같은 문장이 이어짐  
    페이지의 `backing`이 무엇인지, `freshly-faulted`나 “약 10개 명령어 뒤”가 어떤 의미인지, 메커니즘이 어떻게 “localize”된다는 건지 이해하기 어려움. 기술 설명이라기보다 **단어를 이어 붙인 글**처럼 보임  
    제대로 된 기술 문서의 예시는 다음과 같음: [https://yifan.lu/2019/01/11/the-first-f00d-exploit/](https://yifan.lu/2019/01/11/the-first-f00d-exploit/)
  - “오버플로는 여전히 오버플로하고, 해제 후 사용은 여전히 해제 후 사용하며, musl 마스크 경쟁은 여전히 경쟁한다”는 대목은 컴퓨터 시 같아 웃김  
    결론은 대략 **Linux 7.0과 musl 1.2.5의 조합**에 문제가 있다는 듯하지만, 재현은 여전히 같은 물리적 Threadripper CPU에서 강한 부하를 줄 때 간헐적으로만 성공하므로 하드웨어 문제를 배제하지 못했음
  - **AI 생성 버그 보고서**를 읽는 건 끔찍함
  - 추적 자체는 훌륭하지만 설명은 말이 안 됨. 추가적인 TLB 플러시는 오류가 될 수 없으며 CPU는 원할 때 언제든 플러시할 수 있음. 실제 오류는 있어서는 안 될 때 **zero-page PTE**가 존재한 데 있는 듯함  
    어색한 순간에 CPU가 마이그레이션되며 발생한 복잡한 경쟁 상태이거나, 페이지 테이블 제거 경로가 잘못된 PTE를 일시적으로 노출한 버그로 보임. zero page의 PFN이 0이라고도 생각하지 않음  
    추측하자면 직접적인 페이지 테이블 제거 과정에서 CPU가 상위 페이징 구조의 캐시된 항목을 통해 이미 해제되고 재사용된 테이블을 읽도록 허용했을 가능성이 있음. 예전에 이런 문제를 디버깅한 적이 있는데 정말 끔찍했음
  - 전형적으로 장황한 **LLM 찌꺼기 분석**임. 맞는 분석일 수도 있지만 자세히 읽기 힘들고, 사람이 같은 분석을 했다면 분량을 5분의 1로 줄였을 것임

- 왜 다른 libc가 아니라 **musl libc에서만** 버그가 발생하는가?
  - 우연일 뿐이며 **한 대의 머신**에서만 발생하기도 함
  - musl 할당자는 새로 페이지 폴트가 발생한 단일 페이지를 애플리케이션에 바로 노출하기 때문일 가능성이 큼. 다른 할당자는 보통 여러 페이지를 한꺼번에 미리 할당하므로 **경쟁 조건의 시간 창**이 더 좁아짐

- 평소라면 musl의 **스레드 스택 크기**를 의심했을 텐데, 커널 버그로 확인된 것인지 궁금함
