Arm v9에서는 태그된 메모리의 하드웨어 구현인 Arm Memory Tagging Extension(MTE)을 도입했습니다.
MTE는 상위 수준에서 추가 메타데이터로 각 메모리 할당/할당 해제에 태그를 지정합니다. 메모리 위치에 태그를 할당하므로 이 메모리 위치를 참조하는 포인터와 연결할 수 있습니다. 런타임 시 CPU는 포인터와 메타데이터 태그가 각 로드 및 저장에서 일치하는지 확인합니다.
Android 12에서는 커널과 사용자 공간 힙 메모리 할당자가 메타데이터로 각 할당을 강화할 수 있습니다. 이렇게 하면 코드베이스에서 가장 일반적인 메모리 안전 버그의 원인이 되는 use-after-free 버그와 buffer-overflow 버그를 감지할 수 있습니다.
MTE 작동 모드
MTE에는 세 가지 작동 모드가 있습니다.
- 동기 모드(SYNC)
- 비동기 모드(ASYNC)
- 비대칭 모드(ASYMM)
동기 모드(SYNC)
이 모드는 성능보다 정확한 버그 감지에 최적화되어 있으며, 높은 성능 오버헤드가 허용될 경우 정밀한 버그 감지 도구로 사용할 수 있습니다. 이 모드를 사용 설정하면 MTE SYNC가 보안 완화 역할을 합니다.
태그 불일치 시 프로세서는 즉시 실행을 취소하고 SIGSEGV
(코드 SEGV_MTESERR
) 및 메모리 액세스와 장애가 발생한 주소에 관한 전체 정보를 사용하여 프로세스를 종료합니다.
테스트 중에 HWASan/KASAN의 대안으로 또는 타겟 프로세스가 취약한 공격 표면을 나타내는 경우 프로덕션 환경에서 이 모드를 사용하는 것이 좋습니다. 또한 ASYNC 모드에서 버그가 있음을 표시하면 런타임 API를 사용하여 SYNC 모드로 실행을 전환함으로써 정확한 버그 신고를 가져올 수 있습니다.
SYNC 모드에서 실행할 경우 Android 할당자는 모든 할당 및 할당 해제에 관한 스택 트레이스를 기록하고 이를 사용하여 메모리 오류 설명(예: use-after-free 또는 buffer-overflow)과 관련 메모리 이벤트의 스택 트레이스가 포함된 더 나은 오류 보고서를 제공합니다. 이러한 보고서를 통해 더 많은 문맥 정보를 얻을 수 있고 더 쉽게 버그를 추적하고 수정할 수 있습니다.
비동기 모드(ASYNC)
이 모드는 버그 신고의 정확성보다 성능에 최적화되어 있으며 메모리 안전 버그를 감지하는 도구(오버헤드 낮음)로 사용할 수 있습니다.
태그 불일치 시 프로세서는 가장 가까운 커널 항목(예: syscall 또는 타이머 중단)까지 실행을 계속하고 여기서 오류 주소나 메모리 액세스를 기록하지 않고 SIGSEGV
(코드 SEGV_MTEAERR
)를 사용하여 프로세스를 종료합니다.
메모리 안전 버그의 밀도가 낮은 것으로 알려진(테스트 중에 SYNC 모드를 사용하면 됨) 잘 테스트된 코드베이스의 프로덕션 환경에서 이 모드를 사용하는 것이 좋습니다.
비대칭 모드(ASYMM)
Arm v8.7-A의 추가 기능인 비대칭 MTE 모드는 메모리 읽기에 관한 동기 검사와 메모리 쓰기에 관한 비동기 검사를 제공하며 성능은 ASYNC 모드의 성능과 유사합니다. 대부분의 경우 비대칭 모드가 ASYNC 모드보다 개선된 것이므로 가능하면 ASYNC 대신 이 모드를 사용하는 것이 좋습니다.
따라서 아래에 설명된 API에는 비대칭 모드가 언급되어 있지 않습니다. 대신 비동기가 요청될 때 항상 비대칭 모드를 사용하도록 OS를 구성할 수 있습니다. 자세한 내용은 'CPU별 기본 MTE 수준 구성' 섹션을 참고하세요.
사용자 공간의 MTE
다음 섹션에서는 시스템 프로세스 및 애플리케이션에 MTE를 사용 설정하는 방법을 설명합니다. MTE는 아래 옵션 중 하나가 특정 프로세스에 설정되어 있지 않으면(아래에서 MTE가 사용 설정된 구성요소 참고) 기본적으로 사용 중지됩니다.
빌드 시스템을 사용하여 MTE 사용 설정
프로세스 전체에 적용되는 MTE는 기본 실행 파일의 빌드 시간 설정으로 제어됩니다. 다음 옵션을 사용하면 개별 실행 파일 또는 소스 트리의 전체 하위 디렉터리에서 이 설정을 변경할 수 있습니다. 설정은 라이브러리에서 또는 실행 파일도 테스트도 아닌 타겟에서 무시됩니다.
1. 특정 프로젝트의 Android.bp
(예)에서 MTE 사용 설정:
MTE 모드 | 설정 |
---|---|
비동기 MTE | sanitize: { memtag_heap: true, } |
동기 MTE | sanitize: { memtag_heap: true, diag: { memtag_heap: true, }, } |
또는 Android.mk:
에서
MTE 모드 | 설정 |
---|---|
Asynchronous MTE |
LOCAL_SANITIZE := memtag_heap |
Synchronous MTE |
LOCAL_SANITIZE := memtag_heap LOCAL_SANITIZE_DIAG := memtag_heap |
2. 제품 변수를 사용하여 소스 트리의 하위 디렉터리에서 MTE 사용 설정:
MTE 모드 | 포함 목록 | 제외 목록 |
---|---|---|
비동기 | PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
동기 | PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS
MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
또는
MTE 모드 | 설정 |
---|---|
비동기 MTE | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
동기 MTE | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
또는 실행 파일의 제외 경로 지정
MTE 모드 | 설정 |
---|---|
비동기 MTE | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
동기 MTE |
예: PRODUCT_CFI_INCLUDE_PATHS
)
와 비슷한 용도
RODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor) PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \ vendor/$(vendor)/projectB
시스템 속성을 사용하여 MTE 사용 설정
위의 빌드 설정은 다음 시스템 속성을 설정하여 런타임에 재정의할 수 있습니다.
arm64.memtag.process.<basename> = (off|sync|async)
여기서 basename
은 실행 파일의 기본 이름을 나타냅니다.
예를 들어 비동기 MTE를 사용하도록 /system/bin/ping
또는 /data/local/tmp/ping
을 설정하려면 adb shell setprop arm64.memtag.process.ping async
를 사용합니다.
환경 변수를 사용하여 MTE 사용 설정
빌드 설정을 재정의하는 또 다른 방법은 환경 변수(MEMTAG_OPTIONS=(off|sync|async)
)를 정의하는 것입니다.
환경 변수와 시스템 속성이 둘 다 정의된 경우 변수가 우선합니다.
애플리케이션에 MTE 사용 설정
지정하지 않으면 MTE가 기본적으로 사용 중지되어 있지만 MTE를 사용하려는 앱은 AndroidManifest.xml
의 <application>
또는 <process>
태그 아래에서 android:memtagMode
를 설정하면 사용할 수 있습니다.
android:memtagMode=(off|default|sync|async)
<application>
태그에 설정된 경우 이 속성은 애플리케이션에서 사용하는 모든 프로세스에 영향을 미치며 <process>
태그를 설정하여 개별 프로세스에 관해 재정의할 수 있습니다.
실험의 경우 호환성 변경사항을 사용하여 매니페스트에서 어떤 값도 지정하지 않는(또는 default
지정) 애플리케이션의 memtagMode
속성 기본값을 설정할 수 있습니다.
이는 전역 설정 메뉴의 System > Advanced > Developer options
> App Compatibility Changes
에서 확인할 수 있습니다. NATIVE_MEMTAG_ASYNC
또는 NATIVE_MEMTAG_SYNC
를 설정하면 특정 애플리케이션에서 MTE가 사용 설정됩니다.
또는 다음과 같이 am
명령어를 사용하여 설정할 수 있습니다.
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name
MTE 시스템 이미지 빌드
개발 및 브링업 중에는 모든 네이티브 바이너리에 MTE를 사용 설정하는 것이 좋습니다. 이렇게 하면 메모리 안전 버그를 조기에 감지할 수 있고 테스트 빌드에서 사용 설정된 경우 실제 사용자 범위가 제공됩니다.
개발 중에 모든 네이티브 바이너리에서 동기 모드로 MTE를 사용 설정하는 것이 좋습니다.
SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m
빌드 시스템의 변수와 마찬가지로 SANITIZE_TARGET
은 환경 변수 또는 make
설정(예: product.mk
파일에서)으로 사용할 수 있습니다.
이렇게 하면 모든 네이티브 프로세스에 MTE가 사용 설정되지만 애플리케이션(zygote64
에서 포크됨)에는 사용 설정되지 않으며 이 경우 MTE는 위의 안내를 따라 사용 설정할 수 있습니다.
CPU별 기본 MTE 수준 구성
일부 CPU에서는 ASYMM 또는 SYNC 모드의 MTE 성능이 ASYNC의 MTE 성능과 유사할 수 있습니다. 따라서 성능 저하 없이 더 엄격한 검사의 오류 감지 이점을 얻으려면 덜 엄격한 검사 모드가 요청될 때 이러한 CPU에 더 엄격한 검사를 사용 설정하는 것이 좋습니다.
기본적으로 ASYNC 모드에서 실행되도록 구성된 프로세스는 모든 CPU에서 ASYNC 모드로 실행됩니다. 특정 CPU에서 SYNC 모드로 이러한 프로세스를 실행하도록 커널을 구성하려면 부팅 시 값 동기화를 sysfs
항목 /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred
에 작성해야 합니다. 이는 init 스크립트를 사용하면 됩니다. 예를 들어 SYNC 모드에서 ASYNC 모드 프로세스를 실행하도록 CPU 0-1을 구성하고 ASYMM 모드에서 실행을 사용하도록 CPU 2-3을 구성하려면 다음을 공급업체 init 스크립트의 init 절에 추가할 수 있습니다.
write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm
SYNC 모드에서 실행되는 ASYNC 모드 프로세스의 Tombstone에는 메모리 오류 위치를 나타내는 정확한 스택 트레이스가 포함됩니다. 그러나 할당 또는 할당 해제 스택 트레이스는 포함되지 않습니다. 이러한 스택 트레이스는 프로세스가 SYNC 모드에서 실행되도록 구성된 경우에만 사용할 수 있습니다.
int mallopt(M_THREAD_DISABLE_MEM_INIT, level)
여기서 level
은 0 또는 1입니다.
malloc에서 메모리 초기화를 사용 중지하고 정확성을 위해 필요한 경우가 아니라면 메모리 태그를 변경하지 않습니다.
int mallopt(M_MEMTAG_TUNING, level)
여기서 level
은 다음과 같습니다.
M_MEMTAG_TUNING_BUFFER_OVERFLOW
M_MEMTAG_TUNING_UAF
태그 할당 전략을 선택합니다.
- 기본 설정은
M_MEMTAG_TUNING_BUFFER_OVERFLOW
입니다. M_MEMTAG_TUNING_BUFFER_OVERFLOW
: 인접한 할당에 고유한 태그 값을 할당하여 선형 버퍼 오버플로 및 언더플로 버그를 확정적으로 감지할 수 있게 합니다. 가능한 태그 값의 절반만 각 메모리 위치에 사용할 수 있으므로 이 모드에서는 use-after-free 버그를 감지할 가능성이 약간 줄어듭니다. MTE는 동일한 태그 그래뉼(16바이트 정렬 청크) 내에서 오버플로를 감지할 수 없으며 이 모드에서도 약간의 오버플로를 놓칠 수 있습니다. 이러한 오버플로는 메모리 손상의 원인이 될 수 없습니다. 한 그래뉼 내의 메모리는 여러 할당에 사용되지 않기 때문입니다.M_MEMTAG_TUNING_UAF
: 공간(버퍼 오버플로) 및 시간(해제 후 사용) 버그를 둘 다 감지할 수 있는 확률이 ~93%로 균일하도록 독립적으로 무작위 태그를 사용 설정합니다.
위에 설명된 API 외에도 숙련된 사용자라면 다음 사항을 알고 있는 것이 좋습니다.
PSTATE.TCO
하드웨어 레지스터를 설정하면 태그 검사(예)를 일시적으로 억제할 수 있습니다. 예를 들어 태그 콘텐츠를 알 수 없는 메모리 범위를 복사하거나 핫 루프에서 성능 병목 현상을 처리하는 경우입니다.M_HEAP_TAGGING_LEVEL_SYNC
를 사용하면 시스템 비정상 종료 핸들러가 할당 및 할당 해제 스택 트레이스와 같은 추가 정보를 제공합니다. 이 기능을 사용하려면 태그 비트에 액세스해야 하며, 신호 핸들러를 설정할 때SA_EXPOSE_TAGBITS
플래그를 전달하여 기능을 사용 설정해야 합니다. 자체 신호 핸들러를 설정하고 알 수 없는 비정상 종료를 시스템에 위임하는 모든 프로그램은 동일한 작업을 실행하는 것이 좋습니다.
커널의 MTE
커널에 MTE 가속 KASAN을 사용 설정하려면 커널을 CONFIG_KASAN=y
, CONFIG_KASAN_HW_TAGS=y
로 구성합니다. 이러한 구성은 Android
12-5.10
부터 GKI 커널에서 기본적으로 사용 설정됩니다
다음 명령줄 인수를 사용하여 부팅 시간에 제어할 수 있습니다.
kasan=[on|off]
: KASAN을 사용 설정하거나 사용 중지합니다(기본값:on
).kasan.mode=[sync|async]
: 동기 모드와 비동기 모드 중에서 선택합니다(기본값:sync
).kasan.stacktrace=[on|off]
: 스택 트레이스 수집 여부입니다(기본값:on
).- 스택 트레이스 컬렉션에는
stack_depot_disable=off
도 필요합니다.
- 스택 트레이스 컬렉션에는
kasan.fault=[report|panic]
: 보고서만 출력할지 아니면 커널도 패닉 상태로 만들지 여부입니다(기본값:report
). 이 옵션과 관계없이 첫 번째로 오류가 보고된 후 태그 검사가 사용 중지됩니다.
권장 용도
브링업, 개발, 테스트 중에는 SYNC 모드를 사용하는 것이 좋습니다. 이 옵션은 환경 변수 또는 빌드 시스템을 통해 모든 프로세스에서 전역적으로 사용 설정해야 합니다. SYNC 모드에서는 버그가 개발 프로세스 초반에 감지되고 코드베이스가 빠르게 안정화되므로 프로덕션 후반에 버그를 감지하는 데 비용을 쓰지 않아도 됩니다.
프로덕션 환경에서는 ASYNC 모드를 사용하는 것이 좋습니다. 그러면 프로세스 내에 메모리 안전 버그가 있는지 감지하고 추가 심층 방어를 위한 오버헤드가 낮은 도구가 제공됩니다. 버그가 감지되면 개발자는 런타임 API를 활용하여 SYNC 모드로 전환하고 샘플링된 사용자 집합에서 정확한 스택 트레이스를 가져올 수 있습니다.
SoC에 CPU별 기본 MTE 수준을 구성하는 것이 좋습니다. 일반적으로 ASYMM 모드는 ASYNC와 성능 특성이 동일하여 거의 항상 선호됩니다. 작은 in-order 코어는 세 가지 모드에서 모두 비슷한 성능을 보이는 경우가 많으며 SYNC를 선호하도록 구성할 수 있습니다.
개발자는 /data/tombstones
, logcat
을 확인하거나 공급업체 DropboxManager
파이프라인에서 최종 사용자 버그를 모니터링하여 비정상 종료가 있는지 확인해야 합니다. Android 네이티브 코드 디버깅에 관한 자세한 내용은 여기 정보를 참고하세요.
MTE 지원 플랫폼 구성요소
Android 12에서는 보안에 중요한 여러 시스템 구성요소가 MTE ASYNC를 사용하여 최종 사용자 비정상 종료를 감지하고 심층 방어를 강화하는 역할을 합니다. 이러한 구성요소는 다음과 같습니다.
- 네트워킹 데몬 및 유틸리티(
netd
제외) - 블루투스, SecureElement, NFC HAL, 시스템 애플리케이션
statsd
데몬system_server
zygote64
(애플리케이션이 MTE 사용을 선택하도록 허용하기 위해)
이러한 타겟은 다음 기준에 따라 선택되었습니다.
- 권한이 있는 프로세스(unprivileged_app SELinux 도메인이 액세스할 수 없는 항목에 액세스할 수 있는 프로세스로 정의됨)
- 신뢰할 수 없는 입력 처리(2의 법칙)
- 허용 가능한 성능 저하(성능 저하로 인해 사용자에게 표시되는 지연 시간은 발생하지 않음)
공급업체는 위에 설명된 기준에 따라 더 많은 구성요소에 관해 프로덕션 환경에서 MTE를 사용 설정하는 것이 좋습니다. 개발 중에 SYNC 모드를 사용하여 이러한 구성요소를 테스트해 수정된 버그를 쉽게 감지하고 ASYNC가 성능에 미치는 영향을 평가하는 것이 좋습니다.
Android는 향후 예정된 하드웨어 디자인의 성능 특성에 따라 MTE가 사용 설정된 시스템 구성요소 목록을 확장할 계획입니다.