검색 여닫기
검색
메뉴 여닫기
531
239
4
2.1천
noriwiki
둘러보기
대문
최근 바뀜
임의의 문서로
미디어위키 도움말
특수 문서 목록
파일 올리기
환경 설정 메뉴 여닫기
notifications
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
user-interface-preferences
한국어
개인 도구
로그인
AddressSanitizer 문서 원본 보기
noriwiki
문서 공유하기
다른 명령
←
AddressSanitizer
문서 편집 권한이 없습니다. 다음 이유를 확인해주세요:
요청한 명령은 다음 권한을 가진 사용자에게 제한됩니다:
사용자
.
문서의 원본을 보거나 복사할 수 있습니다.
[[분류: 소프트웨어 기반 보안]] = 개요 = '''AddressSanitizer''' ('''ASan''')는 C/C++ 프로그램에서 발생하는 메모리 오류를 탐지하기 위한 메모리 오류 검사 도구이다. AddressSanitizer는 다음과 같은 메모리 오류를 탐지할 수 있다: * Use-after-free * Heap buffer overflow * Stack buffer overflow * Global buffer overflow * Use-after-return * Use-after-scope * Initialization order bugs * Memory leaks == 작동 원리 == === RedZone & Poisoning === AddressSanitizer는 런타임 라이브러리를 통해 <code>malloc</code> 및 <code>free</code> 함수를 대체한다. 이 라이브러리는 메모리의 할당 및 해제 영역 주위에 "레드존(red zone)"이라 불리는 보호 구역을 추가하며, RedZone영역은 Poisoned 처리되어서, 만약 Application이 Poisoned된 영역에 접근할 경우 에러를 리포트한다. * <code>malloc</code> 호출 시, 할당된 영역 주변에 보호용 바이트(레드존)를 삽입하고 이를 포이즌 처리한다. * <code>free</code> 호출 후 해당 메모리 블록은 "격리(quarantine)" 상태로 두고, 여전히 포이즌된 상태를 유지한다. 이는 해제된 메모리에 잘못 접근할 경우 탐지를 가능하게 한다 (Use-after-free 오류). 컴파일러는 프로그램의 모든 메모리 접근을 다음과 같이 변경한다: '''변경 전:''' <syntaxhighlight lang="c"> *address = ...; // 또는 ... = *address; </syntaxhighlight> '''변경 후:''' <syntaxhighlight lang="c"> if (IsPoisoned(address)) { ReportError(address, kAccessSize, kIsWrite); } *address = ...; // 또는 ... = *address; </syntaxhighlight> 여기서: * <code>IsPoisoned(address)</code>: 해당 주소가 포이즌된 영역인지 확인한다. * <code>ReportError</code>: 오류가 감지되면 접근 주소, 크기, 쓰기 여부 등의 정보를 포함한 상세 보고를 생성한다. * <code>IsPoisoned</code> 함수는 매우 빠르게 작동해야 하며, 런타임 오버헤드를 최소화하는 것이 중요하다. * <code>ReportError</code> 함수는 컴팩트하게 구현되어야 하며, 에러 메시지는 디버깅에 유용한 정보를 제공해야 한다. AddressSanitizer는 프로그램 전체의 메모리 레이아웃과 별도의 "섀도우 메모리"를 활용하여 포이즌 상태를 추적한다. === Shadow Memory === AddressSanitizer는 가상 주소 공간을 두 가지 상호 배타적인 구역으로 나눈다: * '''메인 애플리케이션 메모리 (Mem)''': 일반적인 프로그램 코드가 사용하는 주 메모리 공간이다. * '''섀도우 메모리 (Shadow)''': 메모리 접근 상태를 추적하기 위한 메타데이터를 저장한다. 이 메모리는 메인 메모리의 각 바이트에 대한 정보를 대응하여 기록한다. 섀도우 메모리는 포이즌 상태(유효하지 않음)를 나타내기 위해 특수한 값을 저장한다. 메인 메모리의 특정 바이트를 포이즌 처리한다는 것은, 해당 바이트에 대응하는 섀도우 메모리 위치에 특수 값을 기록한다는 의미이다. 섀도우 메모리의 핵심 특징은 메인 메모리와의 빠른 주소 변환에 있다. AddressSanitizer는 아래와 같은 방식으로 주소 변환을 수행한다: <syntaxhighlight lang="c"> shadow_address = MemToShadow(address); </syntaxhighlight> 변환 함수 <code>MemToShadow</code>는 일반적으로 시프트 및 오프셋 연산을 통해 매우 빠르게 수행된다. 예를 들어: <syntaxhighlight lang="c"> // 예시 변환식: shadow = (mem >> 3) + offset; // 8바이트마다 Shadow Memory영역이 할당되었을 경우. </syntaxhighlight> 이는 메모리 접근 시점마다 동적으로 섀도우 주소를 계산할 수 있도록 한다. 이를 통해서 컴파일러는 다음과 같은 코드를 프로그램에 삽입한다. <syntaxhighlight lang="c"> shadow_address = MemToShadow(address); if (ShadowIsPoisoned(shadow_address)) { ReportError(address, kAccessSize, kIsWrite); } </syntaxhighlight> * <code>ShadowIsPoisoned</code>는 섀도우 메모리 값을 검사하여 해당 접근이 유효한지 판단한다. * <code>ReportError</code>는 포이즌된 메모리 접근을 감지한 경우 오류를 보고한다. == 섀도우 메모리 매핑 규칙 == AddressSanitizer는 애플리케이션 메모리 8바이트(qword)를 섀도우 메모리의 1바이트로 매핑한다. 이로 인해 섀도우 메모리의 각 바이트는 8바이트 단위의 메모리 접근 가능 상태를 요약해서 표현한다. 정렬된 8바이트(qword)에 대해 가능한 섀도우 값은 총 9가지뿐이다: # '''0''' – 해당 qword의 모든 바이트가 접근 가능 (unpoisoned). # '''음수''' – 해당 qword의 모든 바이트가 접근 불가능 (fully poisoned). # '''1~7''' – 처음 k 바이트는 접근 가능하고, 나머지 (8-k) 바이트는 접근 불가능. 이는 Malloc으로 할당된 메모리 영역은 주로 Sequential하게 할당된다는 사실에 의거한다. 따라서 대부분의 경우 Malloc의 마지막 8바이트만이, 1부터 7의 값을 가진다. 예: <code>malloc(13)</code>을 호출하면, 다음과 같은 섀도우 상태가 된다: * 첫 번째 qword: 8바이트 모두 접근 가능 → 섀도우 값: 0 * 두 번째 qword: 5바이트 접근 가능, 3바이트는 포이즌 → 섀도우 값: 5 메모리 접근 시 AddressSanitizer가 삽입하는 코드: <syntaxhighlight lang="c"> byte *shadow_address = MemToShadow(address); byte shadow_value = *shadow_address; if (shadow_value) { if (SlowPathCheck(shadow_value, address, kAccessSize)) { ReportError(address, kAccessSize, kIsWrite); } } </syntaxhighlight> * <code>MemToShadow</code>: 주소를 섀도우 메모리 영역으로 변환 * <code>shadow_value</code>: 접근하려는 메모리 영역의 상태 (0, 1~7, 음수) * <code>SlowPathCheck</code>: 실제 접근이 유효한지 정밀하게 판단 여기서 SLowPathCheck은 다음과 같은 코드로 작성될 수 있다. 이 함수는 접근하려는 마지막 바이트가 포이즌된 영역에 걸쳐 있는지를 검사한다. <syntaxhighlight lang="c"> bool SlowPathCheck(byte shadow_value, uintptr_t address, size_t kAccessSize) { size_t last_accessed_byte = (address & 7) + kAccessSize - 1; return (last_accessed_byte >= shadow_value); } </syntaxhighlight> 추가적으로 Application이 Shadow Memory를 접근하여 ASan을 회피하는 시도를 감지하기 위해서, AddressSanitizer는 섀도우 메모리와 애플리케이션 메모리 사이의 변환 함수를 역방향으로 호출(<code>MemToShadow(ShadowAddr)</code>)할 경우, 접근이 불가능한 "ShadowGap"이라는 영역에 도달하게 만든다. 이로 인해 프로그램이 섀도우 메모리 자체에 잘못 접근하려고 하면, 즉시 충돌(segfault)이 발생한다.
AddressSanitizer
문서로 돌아갑니다.