피드로 돌아가기
Dev.toBackend
원문 읽기
PHP C Extension Segfault, 메모리 오염의 원인과 추적 과정
Déboguer un segfault dans une extension PHP/C : l'histoire d'un pointeur fantôme
AI 요약
Context
PHP 객체 그래프를 배열로 직렬화하는 php-ext-deepclone 확장 모듈 개발 중 발생한 문제. 47개 이상의 노드를 가진 연결 리스트 처리 시 Segmentation fault 발생. C 언어 특성상 PHP 수준의 Exception이나 Stack Trace 없이 프로세스가 즉시 종료되는 구조.
Technical Solution
- 재귀 호출로 인한 Call Stack 부족 가능성을 검토하고 재귀 깊이 제한을 12에서 512로 상향 조정
- otool을 통한 스택 프레임 크기 측정 결과 각 수준당 480 octets 소모 확인 및 전체 사용량 22 Ko로 스택 오버플로우 가능성 배제
- fprintf를 활용한 단계적 로깅으로 dc_build_output 함수 내 e->cidx 읽기 시점에서 크래시 지점 특정
- 쓰기 시점의 유효 주소(0x10468e4d0)가 읽기 시점에 정수 값(0x11)으로 변질된 메모리 오염 현상 발견
- 동적 배열(ctx->entries[]) 확장 시 기존 요소의 메모리 주소가 변경되어 이전에 저장한 포인터가 무효화되는 'Dangling Pointer' 문제 식별
- 위험 구간 이후에 배열에 삽입하는 방식으로 로직을 수정하여 메모리 주소 불일치 해결
Impact
- Call Stack 사용량: 약 22 Ko (기본 제한 8 Mo 대비 매우 낮은 수준)
- 재귀 깊이 제한: 12 $\rightarrow$ 512 상향
Key Takeaway
동적 배열의 크기가 확장될 때 기존 요소의 메모리 주소가 변경될 수 있음을 인지하고, 포인터를 직접 저장하기보다 인덱스를 사용하거나 삽입 시점을 제어하는 설계가 필요함.
실천 포인트
C/C++ 기반 확장 모듈 개발 시 메모리 오염 추적을 위해 AddressSanitizer(ASan) 도입을 검토하고, 동적 배열 확장 시 포인터 무효화 가능성을 항상 고려할 것