전체 컴파일 파이프라인
핵심은 컴파일러 드라이버가 모든것을 연결한다는 점이다.
1. 프론트엔드 호출 (소스 → AST → IR)
2. 패스 스케줄링 (어떤 패스를 어떤 순서로 실행할지)
3. 런타임 라이브러리 링킹 (compiler-rt 연결)

- Clang Frontend: 소스코드 → AST → LLVM IR 생성
- LLVM: IR에 패스 적용 후 백엔드에서 기계어 생성
- Compiler-rt: 런타임 라이브러리 링킹
- Sanitizer pass는 항상 마지막에 실행
- 다른 최적화 패스가 sanitizer가 삽입한 코드를 변경하거나 제거할 수 있기 때문
CodeGen 타입 변환
int x = 42;
C/C++ 타입이 LLVM 타입으로 어떻게 변환되는지

| C 타입 | LLVM 타입 | 크기 |
| int | i32 | 32비트 |
| char | i8 | 8비트 |
| bool | i8 | 8비트 |
| long | i64 | 64비트 |
| int* | i32* | 포인터 |
Sanitizer가 타입 정보를 활용해서 메모리 접근을 검사할 수 있다. (예를 들어 "이 포인터는 i32를 가리켜야 하는데 i8처럼 접근하고 있다" 같은 것을 탐지)
Pass Manager
Clang CodeGen이 패스 순서 결정한다. pass manager가 IR을 받아서 패스들을 순서대로 실제 실행한다.

0. 원본 코드
void foo() { bar(); }
void bar() { /* ... */ }
1. Function Inlining 후
void foo() { /* bar의 내용이 여기에 */ }
2. Loop Unrolling 후
// (루프가 있다면 펼쳐짐)
3. Sanitizer Pass 후
void foo() {
__sanitizer_check(); // ← 삽입됨
/* bar의 내용 */
__sanitizer_check(); // ← 삽입됨
}
만약 Sanitizer가 먼저 실행되면 Inlining이 sanitizer 체크 코드를 이상하게 변형시키거나, 최적화가 체크 코드를 "불필요하다"고 판단해 제거할 수 있다. 따라서 패스 실행 순서가 중요하다.
Sanitizer 계측 + 런타임 링킹

1단계: Pass가 함수 호출 삽입
[Before] [After]
┌────────────────┐ ┌────────────────┐
│ Function │ │ Function │
│ BasicBlock │ │ BasicBlock │
│ Inst │ ──────▶ │ call SanFunc │ ← 새로 삽입!
│ Inst │ │ Inst │
│ call Malloc │ │ Inst │
└────────────────┘ │ call Malloc │
└────────────────┘
2단계: 링킹 시 함수 연결
┌─────────────┐ ┌─────────────────┐
│ MyFile.o │ │ Compiler-RT │
├─────────────┤ ├─────────────────┤
│ SanFunc ────┼──── 링킹 ────┼→ SanFunc 구현체 │
│ Malloc ────┼── 가로채기 ──┼→ Interceptor_ │
│ │ │ Malloc │
└─────────────┘ └─────────────────┘
함수 가로채기(Interception)는 링커의 심볼 해석 순서를 이용한 트릭이다. (compiler-rt → libc)
// 원래 프로그램
ptr = malloc(100);
// Sanitizer가 가로챈 후 실제 실행되는 것
ptr = interceptor_malloc(100); // compiler-rt 함수
// interceptor_malloc 내부
void* interceptor_malloc(size_t size) {
__sanitizer_before_malloc(size); // 검사
void* ptr = real_malloc(size); // 진짜 malloc 호출
__sanitizer_after_malloc(ptr); // 추적
return ptr;
}
정리하자면 Sanitizer를 만들려면 수정할 곳은
| 컴포넌트 | 위치 | 역할 |
| Pass | llvm/lib/Transforms/ | IR에 계측 코드 삽입 |
| 드라이버 | clang/lib/Driver/ | -fsanitize=xxx 옵션 처리 |
| CodeGen | clang/lib/CodeGen/ | 패스 등록, 순서 지정 |
| Runtime | compiler-rt/lib/ | 실제 검사 로직, 함수 가로채기 |
References
- https://faculty.sist.shanghaitech.edu.cn/faculty/songfu/course/spring2018/CS131/llvm.pdf
- https://www.cs.cornell.edu/~asampson/blog/llvm.html
- https://eli.thegreenplace.net/
- https://compilers.iecc.com/crenshaw/
- https://blog.trailofbits.com/2019/06/25/creating-an-llvm-sanitizer-from-hopes-and-dreams/
- https://blog.trailofbits.com/2024/05/16/understanding-addresssanitizer-better-memory-safety-for-your-code/
'Security > Fuzzing' 카테고리의 다른 글
| Linux KCOV(Kernel Coverage) (0) | 2026.01.02 |
|---|---|
| syzbot, qemu, gdb를 사용하여 linux kernel의 버그 수정 (0) | 2025.12.12 |
| syzkaller를 이용한 커널 퍼징 #4 실제 커널 퍼징 (0) | 2025.12.11 |
| syzkaller를 이용한 커널 퍼징 #3 syz-manager 설정 및 syz-executor 실행 (0) | 2025.12.10 |
| syzkaller를 이용한 커널 퍼징 #2 Syzkaller의 동작 구조 (0) | 2025.12.10 |