Address Sanitizer를 사용한 메모리 손상 디버그

이 문서에서는 AGDE를 사용할 때 특수 디버깅 도구를 사용 설정하는 방법을 보여줍니다. 이러한 도구는 진단하기 어려운 메모리 손상 및 덮어쓰기 오류를 디버깅하는 데 도움이 될 수 있습니다.

HWAddress Sanitizer 및 Address Sanitizer

HWAddress Sanitizer(HWASan) 및 Address Sanitizer(ASan)는 다음과 같은 메모리 손상 및 덮어쓰기 오류를 디버깅하는 데 도움이 되는 메모리 손상 디버깅 도구입니다.

  • 스택 버퍼 오버플로 및 언더플로
  • 힙 버퍼 오버플로 및 언더플로
  • 범위를 벗어난 스택 사용
  • 더블 프리 및 와일드 프리 오류
  • 반환 후 스택 사용(HWASan만 해당)

HWASan 또는 ASan은 문제를 디버깅할 때나 자동 테스트의 일부로만 사용 설정하는 것이 좋습니다. 이들 도구는 성능이 우수하지만 사용할 경우 불이익이 발생합니다.

런타임 동작

HWASan 및 ASan은 사용 설정되면 앱에서 메모리 손상이 있는지 자동으로 확인합니다.

메모리 오류가 감지되면 앱이 SIGBART(신호 중단) 오류와 함께 비정상 종료되고 logcat에 자세한 메시지를 출력합니다. /data/tombstones의 파일에도 메시지 사본이 작성됩니다.

오류 메시지는 다음과 유사합니다.

ERROR: HWAddressSanitizer: tag-mismatch on address 0x0042a0826510 at pc 0x007b24d90a0c
WRITE of size 1 at 0x0042a0826510 tags: 32/3d (ptr/mem) in thread T0
    #0 0x7b24d90a08  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x2a08)
    #1 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)
    #2 0x7b8f1db364  (/apex/com.android.art/lib64/libart.so+0x18f364)
    #3 0x7b8f2ad8d4  (/apex/com.android.art/lib64/libart.so+0x2618d4)

0x0042a0826510 is located 0 bytes to the right of 16-byte region [0x0042a0826500,0x0042a0826510)
allocated here:
    #0 0x7b92a322bc  (/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so+0x212bc)
    #1 0x7b24d909e0  (/data/app/com.example.hellohwasan-eRpO2UhYylZaW0P_E0z7vA==/lib/arm64/libnative-lib.so+0x29e0)
    #2 0x7b8f1e4ccc  (/apex/com.android.art/lib64/libart.so+0x198ccc)

기본 요건

HWASan 요구사항

HWASan을 사용하려면 다음 단계를 따르세요.

  • AGDE 24.1.99 이상을 사용해야 합니다.
  • 앱은 NDK 26 이상을 사용하여 빌드해야 합니다.
  • 앱은 타겟 SDK 34 이상으로 빌드되어야 합니다.
  • 타겟은 Android 14 (API 수준 34) 이상을 실행하는 arm64-v8a 기기여야 합니다.

프로젝트에서 공유 C++ 표준 라이브러리 사용

알려진 문제로 인해, ASan은 libc++_static 사용 시 C++ 예외 처리와 호환되지 않습니다. libc++_shared를 사용하는 경우에는 이 문제가 나타나지 않습니다.

HWASan에는 연산자 newdelete가 자체적으로 구현되어 있는데, 이러한 연산자는 표준 라이브러리가 프로젝트에 정적으로 연결된 경우 사용할 수 없습니다.

이 설정을 변경하려면 이 문서의 C++ 표준 라이브러리 연결 섹션을 참고하세요.

프레임 포인터 생성 사용 설정

HWASan 및 ASan은 빠른 프레임 포인터 기반 언와인더를 사용하여 메모리 할당 및 할당 해제 이벤트의 스택 트레이스 정보를 생성합니다. 즉, 이러한 기능을 사용하려면 C++ 컴파일러 설정에서 프레임 포인터 생성을 사용 설정해야 합니다. 다시 말하면, 프레임 포인터 생략 최적화를 사용 중지해야 합니다.

이 설정을 변경하려면 이 문서의 프레임 포인터 생성 사용 설정 섹션을 참고하세요.

HWASan 또는 ASan을 사용하도록 Visual Studio 프로젝트 구성

HWASan 또는 ASan 사용 설정

HWASan 또는 ASan을 사용 설정하려면 프로젝트의 속성 페이지에서 구성 속성 > 일반으로 이동합니다.

현재 프로젝트의 Visual Studio 솔루션 탐색기 속성 메뉴

그림 1: Visual Studio 솔루션 탐색기 창에 있는 프로젝트의 속성 옵션

일반 속성이 표시되어 있고 Address Sanitizer 설정이 강조표시되어 있는 프로젝트 속성 페이지 대화상자.

그림 2: 일반 프로젝트 속성의 Address Sanitizer(ASan) 설정

프로젝트에서 HWASan을 사용 설정하려면 Address Sanitizer(ASan) 설정을 Hardware ASan 사용(fsanitize=hwaddress)으로 변경합니다.

프로젝트에서 ASan을 사용 설정하려면 Address Sanitizer(ASan) 설정을 ASan 사용(fsanitize=address)으로 변경합니다.

프레임 포인터 생성 사용 설정

프레임 포인터 생성은 프레임 포인터 생략 C/C++ 컴파일러 설정에 의해 제어되며 프로젝트 속성 페이지구성 속성 > C/C++ > 최적화에서 찾을 수 있습니다.

C/C++ 최적화 속성이 표시되어 있고 프레임 포인터 생략 설정이 강조표시되어 있는 프로젝트 속성 페이지 대화상자.

그림 3: 프레임 포인터 생략 설정을 찾을 수 있는 위치

HWASan 또는 ASan을 사용할 때는 프레임 포인터 생략 설정을 아니요(-fno-omit-frame-pointer)로 설정합니다.

공유 라이브러리 모드에서 C++ 표준 라이브러리 연결

C++ 표준 라이브러리의 링커 모드 설정은 프로젝트 속성 페이지구성 속성 > 일반프로젝트 기본값 섹션에서 찾을 수 있습니다.

일반 카테고리가 선택되어 있고 STL 사용 설정이 강조표시되어 있는 프로젝트 속성 페이지 대화상자.

그림 4: C++ 표준 라이브러리의 링커 모드 설정을 찾을 수 있는 위치

HWASan 또는 ASan을 사용할 때는 STL 사용C++ 표준 라이브러리(.so) 사용으로 설정합니다. 이 값은 C++ 표준 라이브러리를 프로젝트에 공유 라이브러리로 연결합니다. 이는 HWASan 및 ASan이 올바르게 작동하는 데 필요합니다.

Address Sanitizer 사용을 위해 빌드 구성 만들기

HWASan 또는 ASan을 일시적으로 사용하려면 이러한 기능을 사용하기 위한 구성을 새로 만드는 것이 번거로울 수 있습니다. 프로젝트가 작거나, 기능을 살펴보는 중이거나, 테스트 중에 발견한 문제에 대응하는 경우가 이러한 상황에 해당할 수 있습니다.

반면에 이 기능이 유용하다고 판단되어 정기적으로 사용하고 싶다면 Teapot 샘플에 나와 있는 것처럼 HWASan 또는 ASan의 새 빌드 구성을 만드는 것이 좋습니다. 예를 들어, 단위 테스트의 일환으로 또는 게임의 야간 스모크 테스트 중에 Address Sanitizer를 정기적으로 실행할 수 있습니다.

별도의 빌드 구성을 만드는 것은 일반적으로 C++ 표준 라이브러리와 정적으로 연결하는 서드 파티 라이브러리를 많이 사용하는 대규모 프로젝트에 특히 유용합니다. 전용 빌드 구성을 만들면 프로젝트 설정을 항상 정확하게 유지할 수 있습니다.

빌드 구성을 만들려면 프로젝트 속성 페이지에서 구성 관리자... 버튼을 클릭한 후 활성 솔루션 구성 드롭다운을 엽니다. 그런 다음 를 선택하고 적절한 이름으로 새 빌드 구성을 만듭니다(예: HWASan 사용).

커스텀 메모리 할당자와 함께 HWASan 사용

HWASan은 포인터에 태그를 삽입하고 태그 불일치를 확인할 수 있도록 malloc (또는 new)를 통해 할당된 메모리를 자동으로 가로챕니다.

그러나 맞춤 메모리 할당자를 사용하는 경우 HWASan은 맞춤 메모리 할당 메서드를 자동으로 가로챌 수 없습니다. 따라서 맞춤 메모리 할당자와 함께 HWASan을 사용하려면 메모리 할당자를 계측하여 HWASan을 명시적으로 호출하세요. 이는 코드 몇 줄만으로 실행할 수 있습니다.

기본 요건

호출해야 하는 HWASan 메서드는 이 헤더에 정의되어 있습니다.

#include "sanitizer/hwasan_interface.h"

메모리 할당 방법 계측

  1. 16바이트 블록 세분화 및 정렬로 객체를 할당합니다. 예를 들어 24바이트 크기의 고정 크기 객체를 제공하는 풀 할당자가 있는 경우 할당을 32바이트로 올림하고 16바이트로 정렬합니다.

  2. 8비트 태그를 생성합니다. 0~16 값은 내부용으로 예약되어 있으므로 태그에서 사용하면 안 됩니다.

  3. HWASan을 사용 설정하여 해당 태그로 메모리 영역 추적을 시작합니다.

    __hwasan_tag_memory((void*) address, tag, size);
    
  4. 포인터의 상위 8비트에 태그를 삽입합니다.

    address = __hwasan_tag_pointer((void*) address, tag);
    

메모리 할당 해제 메서드 계측

  1. 기존 태그된 포인터를 통한 추가 액세스가 실패하도록 메모리 영역의 태그를 재설정합니다.

    __hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), 0, size);
    

사전 할당된 객체 풀로 작업

메모리 할당자가 풀에서 객체를 사전 할당하고 실제로 해제하는 대신 객체를 풀로 다시 반환하는 경우 할당 해제 메서드는 메모리 및 포인터의 태그를 새 값으로 직접 덮어쓸 수 있습니다.

```
__hwasan_tag_memory(__hwasan_tag_pointer(ptr, 0), tag, size);
ptr = __hwasan_tag_pointer((void*)ptr, tag);
```

이 기법을 사용하면 할당 메서드에서 포인터나 메모리 블록에 태그를 지정할 필요가 없으며 풀에서 객체를 사전 할당할 때 포인터와 메모리 블록에 태그를 지정합니다. 이 스타일을 사용하는 예는 PoolAllocator 샘플을 참고하세요.