# AddressSanitizer

## AddressSanitizer 란?

{% hint style="danger" %}
**경고:** arm64에서 플랫폼 개발을 위한 ASan은 Android 11 후 AOSP maser에서 지원 중단되므로, 대신 [HWASan](https://source.android.com/devices/tech/debug/hwasan?hl=ko)을 사용합니다.
{% endhint %}

AddressSanitizer(ASan)는 네이티브 코드의 메모리 버그 감지를 위한 빠른 컴파일러 기반 도구입니다.

ASan은 다음을 감지합니다.

* 스택 및 힙 버퍼 오버플로우/언더플로우
* 프리 후 힙 사용
* 범위를 벗어난 스택 사용
* 더블 프리/와일드 프리

ASan은 32비트 및 64비트ARM과 x86 및 x86-64에서 실행됩니다. ASan의 CPU 오버헤드는 약 2x, 코드 크기 오버헤드는 50%와 2x 사이이며, 대량 메모리 오버헤드는 할당 패턴에 따라 다르지만 대략 2x 정도입니다.

AArch64의 Android 10 및 AOSP 마스터 분기는 [하드웨어 가속 ASan(HWASan)](https://source.android.com/devices/tech/debug/hwasan?hl=ko)을 지원합니다. 이는 RAM 오버헤드는 낮지만 감지된 버그의 범위는 더 큰 유사 도구입니다. HWASan은 ASan에 의해 감지된 버그 이외에 반환 이후의 스택 사용도 감지합니다.

HWASan은 CPU 및 코드 크기 오버헤드가 비슷하지만 RAM 오버헤드는 15%로 훨씬 작습니다. HWASan은 비확정적입니다. 가능한 태그 값은 256개가 전부이므로 버그를 놓칠 확률은 0.4%로 고정됩니다. HWASan에는 ASan처럼 오버플로우 감지를 위한 제한된 크기의 레드존과 use-after-free 감지를 위한 제한된 용량 격리 저장소가 없습니다. 따라서 오버플로우 크기나 메모리가 할당 취소된 시점은 HWASan과 상관이 없습니다. 이는 HWASan이 ASan보다 나은 이유입니다. [HWASan 설계](http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html) 또는 [Android의 HWASan](https://source.android.com/devices/tech/debug/hwasan?hl=ko) 사용 방법에 관해 자세히 알아보세요.

ASan은 힙 오버플로우 외에 스택/전역 오버플로우도 감지하며, 최소 메모리 오버헤드 덕분에 빠릅니다.

이 문서에서는 ASan으로 Android의 일부/전체를 빌드하고 실행하는 방법에 관해 설명합니다. ASan으로 SDK/NDK 앱을 빌드하는 경우에는 [Address Sanitizer](https://developer.android.com/ndk/guides/asan?hl=ko)를 참조하세요.

## ASan으로 개별 실행 파일 완전 삭제 <a href="#sanitizing_individual_executables_with_asan" id="sanitizing_individual_executables_with_asan"></a>

실행 파일의 빌드 규칙에 `LOCAL_SANITIZE:=address` 또는 `sanitize: { address: true }`를 추가합니다. 코드에서 기존 예를 검색하거나 다른 가용한 새니타이저를 찾을 수 있습니다.

버그가 감지되면 ASan은 상세 보고서를 표준 출력 및 `logcat`에 출력한 다음 프로세스를 비정상 종료합니다.

## ASan으로 공유 라이브러리 완전 삭제 <a href="#sanitizing_shared_libraries_with_asan" id="sanitizing_shared_libraries_with_asan"></a>

ASan으로 빌드된 라이브러리는 ASan으로 빌드된 실행 파일만 사용 가능하며, 이는 ASan의 작동 방식 때문입니다.

{% hint style="info" %}
**참고:** ASan 라이브러리가 잘못된 프로세스에 로드되는 런타임 상황에서는 `_asan` 또는 `_sanitizer`로 시작되는 해결되지 않은 기호 메시지가 표시됩니다.
{% endhint %}

일부만 ASan으로 빌드된 여러 실행 파일에 사용된 공유 라이브러리를 완전히 삭제하려면 라이브러리의 사본 2개가 필요합니다. 권장되는 방법은 다음을 해당 모듈의 `Android.mk`에 추가하는 것입니다.

```
LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan
```

이렇게 하면 라이브러리가 `/system/lib` 대신 `/system/lib/asan`에 놓입니다. 이어서 다음을 사용하여 실행 파일을 실행합니다.

```
LD_LIBRARY_PATH=/system/lib/asan
```

시스템 데몬의 경우 다음을 `/init.rc` 또는 `/init.$device$.rc`의 적절한 섹션에 추가합니다.

```
setenv LD_LIBRARY_PATH /system/lib/asan
```

{% hint style="warning" %}
**경고:** `LOCAL_MODULE_RELATIVE_PATH` 설정은 라이브러리를 `/system/lib/asan`으로 **이동**합니다. 즉, 처음부터 클로버링 및 재구성할 경우 라이브러리가 `/system/lib`에서 누락되며 이미지가 부팅되지 않을 가능성이 높습니다. 이는 현재 빌드 시스템의 안타까운 한계입니다. 클로버링하지 말고 대신 `make -j $N` 및 `adb sync`를 사용하세요.
{% endhint %}

프로세스가 존재하는 경우 `/proc/$PID/maps`를 읽어 프로세스가 `/system/lib/asan`의 라이브러리를 사용 중인지 확인하세요. 사용하지 않는 경우 SELinux를 사용 중지해야 할 수도 있습니다.

```
adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.
```

## 향상된 스택 트레이스 <a href="#better_stack_traces" id="better_stack_traces"></a>

ASan은 빠른 프레임 포인터 기반 언와인더를 사용하여 프로그램의 모든 메모리 할당 및 할당 해제 이벤트의 스택 트레이스를 기록합니다. 대부분의 Android는 프레임 포인터 없이 빌드됩니다. 따라서 한두 개의 의미 있는 프레임만 수신되는 경우가 많습니다. 이를 해결하려면 ASan(권장됨) 또는 다음으로 라이브러리를 재구성하세요.

```
LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm
```

아니면 프로세스 환경에서 `ASAN_OPTIONS=fast_unwind_on_malloc=0`을 설정합니다. 두 번째 방법은 로드에 따라 CPU를 많이 사용할 수도 있습니다.

## 기호화 <a href="#symbolization" id="symbolization"></a>

처음에는 ASan 보고서에 바이너리 및 공유 라이브러리의 오프셋에 대한 참조가 포함되었습니다. 소스 파일과 행 정보를 가져오는 방법에는 두 가지가 있습니다.

* `llvm-symbolizer` 바이너리가 `/system/bin`에 있는지 확인합니다. `llvm-symbolizer`는 `third_party/llvm/tools/llvm-symbolizer`의 소스에서 빌드됩니다.
* `external/compiler-rt/lib/asan/scripts/symbolize.py` 스크립트를 통해 보고서를 필터링합니다.

두 번째 방식을 사용하면 호스트에서 기호화된 라이브러리의 가용성 때문에 더 많은 데이터(`file:line` 위치)를 제공할 수 있습니다.

## 앱의 ASan <a href="#addresssanitizer_in_the_apps" id="addresssanitizer_in_the_apps"></a>

ASan은 자바 코드의 내용을 볼 수 없지만 JNI 라이브러리의 버그는 감지할 수 있습니다. 이를 위해서는 ASan으로 실행 파일을 빌드해야 하며 이 경우에는 `/system/bin/app_process(`*`32|64`*`)`입니다. 이렇게 하면 ASan이 기기의 모든 앱에 동시에 사용 설정됩니다. 이는 상당히 큰 로드이지만 2 GB RAM을 보유한 기기 정도면 충분히 처리할 수 있습니다.

`LOCAL_SANITIZE:=address`를 `frameworks/base/cmds/app_process`의 `app_process` 빌드 규칙에 추가합니다. 일단은 같은 파일의 `app_process__asan` 타겟을 무시합니다(읽는 시점에도 계속해서 있는 경우).

관련 `system/core/rootdir/init.zygote(`*`32|64`*`).rc` 파일에 있는 `service zygote` 섹션을 수정하여 `class main`이 포함되어 있으며 들여쓰기된 행의 블록에 다음 행을 추가하고 동일하게 들여쓰기하세요.

```
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
setenv ASAN_OPTIONS allow_user_segv_handler=true
```

빌드하고, adb sync를 실행하고, fastboot 플래시 부팅을 한 후 재부팅합니다.

## 래핑 속성 사용 <a href="#using_the_wrap_property" id="using_the_wrap_property"></a>

이전 섹션의 접근 방식을 사용하면 ASan이 시스템의 모든 앱에 삽입됩니다(실질적으로는 Zygote 프로세스의 모든 하위 프로세스에 삽입됨). ASan으로 1개 또는 여러 앱을 실행하여 일부 메모리 오버헤드를 느려진 앱 시작과 맞교환하는 것이 가능합니다.

이를 위해서는 앱을 `wrap.` 속성으로 시작해야 합니다. 다음 예시는 ASan 밑에서 Gmail 앱을 실행합니다.

```
adb root
adb shell setenforce 0  # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
```

이 컨텍스트에서는 `asanwrapper`가 `/system/bin/app_process`를 ASan으로 빌드된 `/system/bin/asan/app_process`에 다시 씁니다. 또한 동적 라이브러리 검색 경로 시작 부분에 `/system/lib/asan`을 추가합니다. 이렇게 하면 `asanwrapper`로 실행할 경우 ASan으로 계측화된 `/system/lib/asan`의 라이브러리가 `/system/lib`의 일반 라이브러리보다 선호됩니다.

버그가 감지되면 앱이 다운되고 로그에 보고서가 출력됩니다.

## SANITIZE\_TARGET <a href="#sanitize_target" id="sanitize_target"></a>

Android 7.0 이상에는 ASan으로 한 번에 전체 Android 플랫폼을 빌드하기 위한 지원 기능이 포함됩니다. Android 9보다 높은 출시를 빌드하는 경우에는 HWASan을 선택하는 것이 좋습니다.

같은 빌드 트리에서 다음 명령어를 실행합니다.

```
make -j42
SANITIZE_TARGET=address make -j42
```

이 모드에서는 `userdata.img`가 추가 라이브러리를 포함하며, 기기에도 플래시되어야 합니다. 다음 명령줄을 사용합니다.

```
fastboot flash userdata && fastboot flashall
```

이렇게 하면 `/system/lib`의 일반 공유 라이브러리 집합(첫 번째 make 호출)과 ASan으로 계측화된 `/data/asan/lib`의 공유 라이브러리 집합(두 번째 make 호출)이 빌드됩니다. 두 번째 빌드의 실행 파일이 첫 번째 빌드의 실행 파일을 덮어씁니다. ASan으로 계측화된 실행 파일은 `/system/lib` 전에 `/data/asan/lib`을 포함하는 다른 라이브러리 검색 경로를 가져오며, 이때 `PT_INTERP`의 `/system/bin/linker_asan`을 사용합니다.

`$SANITIZE_TARGET` 값이 변경되면 빌드 시스템은 중간 객체 디렉터리를 클로버링합니다. 이렇게 하면 모든 타겟의 재구성이 강제되는 동시에 `/system/lib` 아래에 설치된 바이너리는 보전됩니다.

일부 타겟은 ASan으로 빌드할 수 없습니다.

* 정적으로 연결된 실행 파일
* `LOCAL_CLANG:=false` 타겟
* `LOCAL_SANITIZE:=false`는 `SANITIZE_TARGET=address`에 관해 ASan이 적용되지 않음

이러한 실행 파일은 `SANITIZE_TARGET` 빌드에서 생략되며 첫 번째 make 호출의 버전이 `/system/bin`에 남습니다.

이러한 라이브러리는 ASan 없이 빌드되며, 종속된 정적 라이브러리에서 가져온 일부 ASan 코드를 포함할 수 있습니다.

## 지원 문서 <a href="#supporting_documentation" id="supporting_documentation"></a>

* [Address Sanitizer](https://developer.android.com/ndk/guides/asan?hl=ko)
* [AddressSanitizer 및 Chromium](https://www.chromium.org/developers/testing/addresssanitizer)
* [기타 Google 새니타이저](https://github.com/google/sanitizers)

## 출처 : [바로가기 ](https://source.android.com/)

{% embed url="<https://doc.skill.or.kr>" %}
NHN Cloud 정보 사이트&#x20;
{% endembed %}

{% embed url="<https://ssv.skill.or.kr>" %}
취약점 진단 분석 평가 방법 사이트
{% endembed %}
