콘텐츠로 이동

C++ Sanitizers — AddressSanitizer, UBSan, ThreadSanitizer

C++ Sanitizer는 컴파일러가 코드에 계측(instrumentation)을 삽입해 런타임에 버그를 탐지하는 도구입니다. 정적 분석이 놓치는 런타임 의존 버그를 테스트 중에 즉시 발견할 수 있습니다.


메모리 관련 버그를 탐지합니다.

탐지 범위: 버퍼 오버플로, use-after-free, use-after-return, 힙/스택/전역 오버플로, double-free

Terminal window
# 컴파일 옵션
g++ -fsanitize=address -fno-omit-frame-pointer -g -O1 main.cpp
clang++ -fsanitize=address -fno-omit-frame-pointer -g -O1 main.cpp
// 탐지 예: use-after-free
int* p = new int(42);
delete p;
std::cout << *p; // ASan이 즉시 오류 보고

ASan 출력 예시:

==ERROR: AddressSanitizer: heap-use-after-free on address 0x...
READ of size 4 at 0x... thread T0
#0 0x... in main main.cpp:5

미정의 동작(UB)을 런타임에 검출합니다.

탐지 범위: 정수 오버플로, null 포인터 역참조, 정렬 위반, 배열 범위 초과, 유효하지 않은 bool 값

Terminal window
g++ -fsanitize=undefined -g -O1 main.cpp
# 특정 검사만 활성화
g++ -fsanitize=signed-integer-overflow,null -g main.cpp
// 탐지 예: 부호 있는 정수 오버플로
int max = INT_MAX;
int overflow = max + 1; // UBSan: signed integer overflow
// 탐지 예: null 역참조
int* p = nullptr;
*p = 42; // UBSan: null pointer dereference

멀티스레드 데이터 레이스를 탐지합니다.

TSan은 ASan과 동시에 사용할 수 없습니다.

Terminal window
g++ -fsanitize=thread -g -O1 main.cpp
// 탐지 예: 데이터 레이스
int counter = 0;
std::thread t1([&]{ counter++; });
std::thread t2([&]{ counter++; });
t1.join(); t2.join();
// TSan: data race on variable counter

TSan 출력 예시:

WARNING: ThreadSanitizer: data race (pid=...)
Write of size 4 at 0x... by thread T2:
#0 main.cpp:6
Previous write of size 4 at 0x... by thread T1:
#0 main.cpp:6

초기화되지 않은 메모리 읽기를 탐지합니다 (Clang 전용).

Terminal window
clang++ -fsanitize=memory -g -O1 main.cpp
int x;
if (x > 0) // MSan: use of uninitialized value
std::cout << "positive\n";

Terminal window
# 개발/테스트 빌드 권장 조합
g++ -fsanitize=address,undefined \
-fno-omit-frame-pointer \
-g -O1 \
main.cpp
# TSan은 별도 빌드 (ASan과 충돌)
g++ -fsanitize=thread -g -O1 main.cpp

CMake 설정:

option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
option(ENABLE_TSAN "Enable ThreadSanitizer" OFF)
if(ENABLE_ASAN)
target_compile_options(myapp PRIVATE -fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_options(myapp PRIVATE -fsanitize=address,undefined)
endif()
if(ENABLE_TSAN)
target_compile_options(myapp PRIVATE -fsanitize=thread)
target_link_options(myapp PRIVATE -fsanitize=thread)
endif()

# Visual Studio 2019 16.9+
/fsanitize=address ← ASan 지원
# UBSan, TSan는 미지원 → Clang-cl 사용

# GitHub Actions 예시
- name: Build with ASan
run: |
cmake -B build -DENABLE_ASAN=ON
cmake --build build
- name: Run tests with ASan
run: |
cd build && ctest --output-on-failure
env:
ASAN_OPTIONS: "detect_leaks=1:abort_on_error=1"
UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"

Terminal window
# 메모리 누수 탐지 활성화
ASAN_OPTIONS=detect_leaks=1 ./myapp
# UBSan 상세 출력
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ./myapp
# TSan 보고 무시 목록
TSAN_OPTIONS=suppressions=tsan.supp ./myapp

Sanitizer는 C++에서 가장 효율적인 버그 검출 도구입니다. CI 파이프라인에 ASan+UBSan 빌드를 추가하고 모든 테스트를 해당 빌드로 실행하면 메모리 오류와 미정의 동작을 배포 전에 잡을 수 있습니다. 성능 오버헤드(2~10×)가 있으므로 테스트 빌드에서만 활성화하세요.