Android NDK는 NDK r21과 Android 10(API 수준 29)부터 HWASan이라고도 하는 HWAddress Sanitizer를 지원합니다. HWASan은 64비트 ARM 기기에서만 사용할 수 있습니다.
HWASan은 ASan과 유사한 메모리 오류 감지 도구입니다. HWASan은 기본 ASan과 비교하여 다음과 같은 이점이 있습니다.
- 유사한 CPU 오버헤드(약 2배)
- 유사한 코드 크기 오버헤드(40~50%)
- RAM 오버헤드가 훨씬 적음(10~35%)
HWASan은 다음과 같이 ASan과 동일한 버그 집합을 감지합니다.
- 스택 및 힙 버퍼 오버플로우 또는 언더플로우
- 프리 후 힙 사용
- 범위를 벗어난 스택 사용
- 더블 프리 또는 와일드 프리
또한 HWASan은 다음을 감지합니다.
- 반환 후 스택 사용
샘플 앱
샘플 앱에서 HWASan용 빌드 변형을 구성하는 방법을 확인할 수 있습니다.
빌드
HWAddress Sanitizer를 사용하여 앱의 네이티브(JNI) 코드를 빌드하려면 다음을 따르세요.
ndk-build
Application.mk
파일에서 다음 코드를 추가하세요.
APP_STL := c++_shared # Or system, or none, but not c++_static.
APP_CFLAGS := -fsanitize=hwaddress -fno-omit-frame-pointer
APP_LDFLAGS := -fsanitize=hwaddress
CMake
모듈의 build.gradle
파일에서 다음 코드를 추가하세요.
android {
defaultConfig {
externalNativeBuild {
cmake {
# Can also use system or none as ANDROID_STL, but not c++_static.
arguments "-DANDROID_STL=c++_shared"
}
}
}
}
CMakeLists.txt의 각 타겟에 다음 코드를 추가하세요.
target_compile_options(${TARGET} PUBLIC -fsanitize=hwaddress -fno-omit-frame-pointer)
target_link_options(${TARGET} PUBLIC -fsanitize=hwaddress)
NDK 27 이상에서는 build.gradle
에서 다음을 사용할 수도 있으며 CMakeLists.txt를 변경할 필요가 없습니다.
android {
defaultConfig {
externalNativeBuild {
cmake {
arguments "-DANDROID_SANITIZE=hwaddress"
}
}
}
}
ANDROID_USE_LEGACY_TOOLCHAIN_FILE=false
를 사용하는 경우에는 작동하지 않습니다.
Android 14 이상 버전: wrap.sh 추가
Android 14 이상 버전을 실행하는 경우 wrap.sh 스크립트를 사용하여 모든 Android 지원 기기에서 디버그 가능한 앱을 실행할 수 있습니다. 설정 안내의 단계를 따르는 경우 이 단계를 건너뛰어도 됩니다.
안내에 따라 wrap.sh 스크립트를 패키징하여 arm64-v8a
에 관한 다음 wrap.sh 스크립트를 추가합니다.
#!/system/bin/sh
LD_HWASAN=1 exec "$@"
실행
Android 버전 14 미만을 실행 중이거나 wrap.sh 스크립트를 추가하지 않은 경우 앱을 실행하기 전에 설정 안내를 따르세요.
평소와 같이 앱을 실행합니다. 메모리 오류가 감지되면 앱이 SIGABRT와 함께 비정상 종료되고 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)
메시지 다음에는 애플리케이션의 실시간 스레드 목록, 근처 메모리 할당 태그, CPU 레지스터 값 등 추가 디버깅 정보가 표시될 수 있습니다.
HWASan 오류 메시지에 관한 자세한 내용은 HWASan 보고서 이해하기를 참고하세요.
명령줄 실행 파일 빌드
Android 14 이상에서는 HWASan으로 계측된 실행 파일을 빌드하고 실행할 수 있습니다. 실행 파일의 경우 ndk-build의 빌드에 설명된 것과 동일한 구성을 사용하거나 CMake를 사용할 수 있습니다. Android 14 이상을 실행하는 기기에 실행 파일을 푸시하고 셸을 사용하여 평소와 같이 실행합니다.
libc++를 사용하는 경우 공유 STL을 사용하고 있는지 확인하고 기기에 푸시한 후 바이너리를 실행할 때 LD_LIBRARY_PATH
를 STL이 포함된 디렉터리로 설정합니다.
Gradle을 사용하지 않는 경우 NDK 문서에서 CMake 및 ndk-build를 사용하여 명령줄에서 빌드하는 방법을 알아보세요.
Android 13 이하: 설정
기기에서 Android 14 이상 버전을 실행하는 경우 이 단계를 건너뛰고 빌드 섹션의 wrap.sh 사용 안내를 따를 수 있습니다. 또는 이 섹션을 따르고 wrap.sh 사용 안내를 건너뛸 수도 있습니다.
Android 14 이전 기기의 경우 HWASan 애플리케이션을 실행하려면 Android의 HWASan 빌드가 필요합니다. 미리 빌드된 HWASan 이미지를 지원되는 Pixel 기기에 플래시할 수 있습니다. 빌드는 ci.android.com에서 제공되며, 여기에서 플래시 빌드 링크를 얻으려는 정확한 빌드의 정사각형을 클릭하면 됩니다. 이때 휴대전화의 코드명을 알고 있어야 합니다.
flash.android.com으로 바로 이동하는 것이 더 쉬울 수 있습니다. 기기 감지와 함께 작업이 시작되고 사용 가능한 빌드만 표시되기 때문입니다. 다음 이미지는 이 도구의 설정 과정을 보여줍니다.
개발자 모드를 사용 설정한 기기를 USB 케이블로 컴퓨터와 연결합니다. 새 기기 추가를 클릭하고 대화상자에서 기기를 선택한 다음 연결을 클릭합니다.
기기가 연결되면 클릭하여 빌드를 구성합니다.
빌드 ID 선택 상자에서 aosp-master-with-phones-throttled
브랜치를 선택하여 연결된 기기에 맞는 이미지가 자동으로 선택되도록 합니다.
설치를 클릭하여 기기를 플래시합니다.
필요한 설정에 관한 자세한 내용은 Android Flash Tool 문서를 참고하세요. 또는 소스에서 HWASan 이미지를 빌드하는 방법을 AOSP 문서에서 확인해 보세요.