Arm Memory Tagging Extension(MTE)

Android 사이트 참고

Arm Memory Tagging Extension(MTE) 란?

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를 사용 설정하면 프로세스의 모든 네이티브 힙 할당에 적용됩니다.

빌드 시스템을 사용하여 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

PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_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_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS

예: 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 모드로 프로토타입을 신속하게 제작하고 실험할 때 사용하는 것이 좋습니다. 이 속성은 자바 애플리케이션에는 적용되지 않습니다. MTE 설정이 AndroidManifest.xml 구성에 기반하기 때문입니다.

환경 변수를 사용하여 MTE 사용 설정

빌드 설정을 재정의하는 또 다른 방법은 환경 변수(MEMTAG_OPTIONS=(off|sync|async))를 정의하는 것입니다. 환경 변수와 시스템 속성이 둘 다 정의된 경우 변수가 우선합니다.

애플리케이션에 MTE 사용 설정

지정하지 않으면 MTE가 기본적으로 사용 중지되어 있지만 MTE를 사용하려는 앱은 AndroidManifest.xml<application> 또는 <process> 태그 아래에서 android:memtagMode를 설정하면 사용할 수 있습니다.

주의: MTE를 사용 설정하는 애플리케이션은 Play 스토어에 제공하기 전에 MTE 호환 기기에서 철저히 테스트해야 합니다. 그러지 않으면 MTE 지원 기기에서만 심각한 버그가 발견되어 앱의 사용성이 저하될 수 있습니다.

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 모드에서 실행되도록 구성된 경우에만 사용할 수 있습니다.

참고: 동기 MTE용으로 구성된 프로세스는 이 설정과 관계없이 항상 SYNC 모드에서 실행됩니다.

참고:

  • 이러한 API를 사용하려면 빌드 시스템이나 시스템 속성, 환경 변수, 앱 매니페스트 플래그, appcompat 기능을 사용하여 프로세스 또는 애플리케이션이 하나 이상의 MTE 모드에 선택되어 있어야 합니다.

  • M_HEAP_TAGGING_LEVEL_ASYNC <-> M_HEAP_TAGGING_LEVEL_SYNC는 여러 번 변경할 수 있습니다.

  • M_HEAP_TAGGING_LEVEL_ASYNC or M_HEAP_TAGGING_LEVEL_SYNC에서 M_HEAP_TAGGING_LEVEL_NONE으로의 전환은 단방향 전환으로, MTE를 영구적으로 사용 중지합니다. M_HEAP_TAGGING_LEVEL_NONE에서 원하는 모드로의 전환은 허용되지 않습니다.

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). 이 옵션과 관계없이 첫 번째로 오류가 보고된 후 태그 검사가 사용 중지됩니다.

참고: 부팅 시간 플래그는 MTE를 지원하는 하드웨어에서 MTE 지원 KASAN에서만 작동합니다. 자세한 내용은 업스트림 KASAN 문서(https://www.kernel.org/doc/html/latest/dev-tools/kasan.html)를 참고하세요.

브링업, 개발, 테스트 중에는 SYNC 모드를 사용하는 것이 좋습니다. 이 옵션은 환경 변수 또는 빌드 시스템을 통해 모든 프로세스에서 전역적으로 사용 설정해야 합니다. SYNC 모드에서는 버그가 개발 프로세스 초반에 감지되고 코드베이스가 빠르게 안정화되므로 프로덕션 후반에 버그를 감지하는 데 비용을 쓰지 않아도 됩니다.

프로덕션 환경에서는 ASYNC 모드를 사용하는 것이 좋습니다. 그러면 프로세스 내에 메모리 안전 버그가 있는지 감지하고 추가 심층 방어를 위한 오버헤드가 낮은 도구가 제공됩니다. 버그가 감지되면 개발자는 런타임 API를 활용하여 SYNC 모드로 전환하고 샘플링된 사용자 집합에서 정확한 스택 트레이스를 가져올 수 있습니다.

SoC에 CPU별 기본 MTE 수준을 구성하는 것이 좋습니다. 일반적으로 ASYMM 모드는 ASYNC와 성능 특성이 동일하여 거의 항상 선호됩니다. 작은 in-order 코어는 세 가지 모드에서 모두 비슷한 성능을 보이는 경우가 많으며 SYNC를 선호하도록 구성할 수 있습니다.

중요: 개발 및 테스트 중에 코드 적용 범위는 모든 사용자 시나리오를 처리할 만큼 크지 않을 수 있습니다. 따라서 프로덕션 환경에서 ASYNC 모드를 사용하는 것은 상호 보완적이며 대부분의 중요한 프로세스에 고려해야 합니다. 또한 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가 사용 설정된 시스템 구성요소 목록을 확장할 계획입니다.

MTE 부트로더 지원

Android 13에서는 요청된 MTE 모드를 부트로더에 전달할 수 있도록 사용자 공간용 ABI를 도입했습니다. 이는 하드웨어를 지원하지만 기본적으로 MTE가 사용 설정된 상태로 제공되지 않는 기기에서 MTE를 사용 설정하거나 기본적으로 MTE가 사용 설정된 상태로 제공되는 기기에서 MTE를 사용 중지하는 데 사용할 수 있습니다.

부트로더 지원

이 ABI를 지원하려면 부트로더가 misc 파티션에서 misc_memtag_message(bootloader_message.h에 정의됨)를 읽어야 합니다. 유효한 misc_memtag_message가 있으면(MISC_VIRTUAL_AB_MAGIC_HEADER가 일치하고 버전 번호가 지원됨) 부트로더가 계산합니다.

memtag = (default_memtag && !(misc.memtag_mode & MISC_MEMTAG_MODE_MEMTAG_OFF)) ||
      misc.memtag_mode & MISC_MEMTAG_MODE_MEMTAG ||
      misc.memtag_mode & MISC_MEMTAG_MODE_MEMTAG_ONCE

memtag_kernel = misc.memtag_mode & MISC_MEMTAG_MODE_MEMTAG_KERNEL ||
      misc.memtag_mode & MISC_MEMTAG_MODE_MEMTAG_KERNEL_ONCE

default_memtag는 SKU의 기본 memtag 사용/사용 중지 설정입니다. memtag가 true인 경우 부트로더는 MTE 태그 예약을 설정하고 하위 예외 수준에서 태그 확인을 사용 설정하며 태그 예약 영역을 DT를 통해 커널에 전달합니다. memtag가 false인 경우 부트로더는 커널 명령줄에 arm64.nomte를 추가합니다.

memtag_kernel이 true인 경우 부트로더는 커널 명령줄에 kasan=on을 추가합니다.

부트로더는 부팅할 때마다 MISC_MEMTAG_MODE_MEMTAG_ONCEMISC_MEMTAG_MODE_MEMTAG_KERNEL_ONCE를 삭제해야 합니다.

부트로더가 fastboot oem mte를 지원하면 다른 플래그를 보존하면서 MISC_MEMTAG_MODE_{MEMTAG, MEMTAG_ONLY, MEMTAG_OFF}(1, 0, 0)으로 설정하여 사용하고 (0, 0, 1)로 설정하여 사용 중지합니다.

사용자 공간 지원을 위한 mtectrl 바이너리를 빌드하도록 제품을 구성해야 합니다. 그런 다음 부트로더가 memtag 메시지를 지원한다고 시스템에 나타내도록 ro.arm64.memtag.bootctl_supported 시스템 속성을 설정합니다.

사용자 인터페이스

시스템 속성

고급 사용을 위해 시스템 속성 arm64.memtag.bootctl은 다음 값의 쉼표로 구분된 목록을 사용할 수 있습니다.

  • memtag: 사용자 공간 MTE를 지속적으로 사용 설정합니다(MISC_MEMTAG_MODE_MEMTAG 설정).

  • memtag-once: 사용자 공간 MTE를 한 번 사용 설정합니다(MISC_MEMTAG_MODE_MEMTAG_ONCE 설정).

  • memtag-kernel: 커널 공간 MTE를 사용 설정합니다(MISC_MEMTAG_MODE_MEMTAG_KERNEL 설정).

  • memtag-kernel-once: 커널 공간 MTE를 한 번 사용 설정합니다(MISC_MEMTAG_MODE_MEMTAG_KERNEL_ONCE).

  • memtag-off: MTE를 사용 중지합니다(MISC_MEMTAG_MODE_MEMTAG_OFF 설정)

새로운 설정을 적용하려면 시스템을 재부팅해야 합니다(부트로더에서 적용함).

MTE 보고서 이해하기

SIGSEGV가 다운되면서 표시되는 코드 9(SEGV_MTESERR), 코드 8(SEGV_MTEAERR)은 메모리 태그 오류입니다. Memory Tagging Extension(MTE)은 Android 12 이상에서 지원되는 Armv9 기능입니다. MTE는 태그된 메모리의 하드웨어 구현입니다. 메모리 안전 버그를 감지하고 완화하는 세분화된 메모리 보호 기능이 있습니다.

C/C++에서 malloc(), new() 연산자 또는 이와 유사한 함수에 대한 호출로부터 반환된 포인터는 할당 범위 내의 메모리에 액세스하는 데만 사용될 수 있으며, 메모리 할당이 활성화된 상태(해제 또는 삭제되지 않음)에서만 가능합니다. Android에서 MTE는 이 규칙의 위반을 감지하기 위해 사용되며, 비정상 종료 보고서에는 위반 내용이 'Buffer Overflow'/'Buffer Underflow'/'Use After Free' 오류로 표시됩니다.

MTE에는 동기('sync') 모드와 비동기('async') 모드가 있습니다. 동기 모드는 느리지만 더 정확한 진단 결과를 보여줍니다. 비동기 모드는 빠르지만 대략적인 진단 결과만 보여줄 수 있습니다. 진단 결과가 다소 다르기 때문에 두 가지 모드를 나누어 설명하겠습니다.

동기 모드 MTE

MTE의 동기('sync') 모드에서는 SIGSEGV가 code 9(SEGV_MTESERR) 메시지를 표시하며 다운됩니다.

pid: 13935, tid: 13935, name: sanitizer-statu  >>> sanitizer-status <<<
uid: 0
tagged_addr_ctrl: 000000000007fff3
signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x800007ae92853a0
Cause: [MTE]: Use After Free, 0 bytes into a 32-byte allocation at 0x7ae92853a0
x0  0000007cd94227cc  x1  0000007cd94227cc  x2  ffffffffffffffd0  x3  0000007fe81919c0
x4  0000007fe8191a10  x5  0000000000000004  x6  0000005400000051  x7  0000008700000021
x8  0800007ae92853a0  x9  0000000000000000  x10 0000007ae9285000  x11 0000000000000030
x12 000000000000000d  x13 0000007cd941c858  x14 0000000000000054  x15 0000000000000000
x16 0000007cd940c0c8  x17 0000007cd93a1030  x18 0000007cdcac6000  x19 0000007fe8191c78
x20 0000005800eee5c4  x21 0000007fe8191c90  x22 0000000000000002  x23 0000000000000000
x24 0000000000000000  x25 0000000000000000  x26 0000000000000000  x27 0000000000000000
x28 0000000000000000  x29 0000007fe8191b70
lr  0000005800eee0bc  sp  0000007fe8191b60  pc  0000005800eee0c0  pst 0000000060001000

backtrace:
      #00 pc 00000000000010c0  /system/bin/sanitizer-status (test_crash_malloc_uaf()+40) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #01 pc 00000000000014a4  /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #02 pc 00000000000019cc  /system/bin/sanitizer-status (main+1032) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #03 pc 00000000000487d8  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+96) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)

deallocated by thread 13935:
      #00 pc 000000000004643c  /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::quarantineOrDeallocateChunk(scudo::Options, void*, scudo::Chunk::UnpackedHeader*, unsigned long)+688) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #01 pc 00000000000421e4  /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::deallocate(void*, scudo::Chunk::Origin, unsigned long, unsigned long)+212) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #02 pc 00000000000010b8  /system/bin/sanitizer-status (test_crash_malloc_uaf()+32) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #03 pc 00000000000014a4  /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)

allocated by thread 13935:
      #00 pc 0000000000042020  /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::allocate(unsigned long, scudo::Chunk::Origin, unsigned long, bool)+1300) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #01 pc 0000000000042394  /apex/com.android.runtime/lib64/bionic/libc.so (scudo_malloc+36) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #02 pc 000000000003cc9c  /apex/com.android.runtime/lib64/bionic/libc.so (malloc+36) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #03 pc 00000000000010ac  /system/bin/sanitizer-status (test_crash_malloc_uaf()+20) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #04 pc 00000000000014a4  /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)

MTE 비정상 종료 보고서에는 문제가 감지된 지점의 레지스터 덤프와 backtrace 정보가 있습니다. MTE가 감지한 오류의 'Cause:' 행에는 위 예와 같이 '[MTE]' 표시와 자세한 내용이 나타납니다. 이때 감지된 오류 유형은 'Use after free'이며 '0 bytes into a 32-byte allocation at 0x7ae92853a0'는 할당의 크기와 주소, 액세스를 시도한 할당에 관한 오프셋을 나타냅니다.

MTE 비정상 종료 보고서에는 감지 지점 외의 추가적인 backtrace 정보도 포함됩니다.

'Use After Free' 오류의 경우 크래시 덤프에 'deallocated by', 'allocated by' 섹션이 추가되며, 이러한 섹션은 메모리 할당 해제 시점(사용 전)과 이전 할당 시점의 스택 트레이스 정보를 보여줍니다. 어떤 스레드에 의해 할당/할당 해제되었는지도 보여줍니다. 간단한 이번 사례에서는 감지 스레드, 할당 스레드, 할당 해제 스레드 모두 동일하지만 더 복잡한 실제 사례에서는 그렇지 않을 수 있습니다. 스레드가 다르다는 것을 숙지하면 동시 실행 관련 버그를 찾는 데 큰 도움이 됩니다.

'Buffer Overflow', 'Buffer Underflow' 오류는 'allocated by' 스택 트레이스만 추가로 보여줍니다. 정의에 따르면 아직 할당 해제되지 않았기 때문입니다. 해제되었다면 'Use After Free' 오류로 나올 것입니다.

Cause: [MTE]: Buffer Overflow, 0 bytes right of a 32-byte allocation at 0x7ae92853a0
[...]
backtrace:
[...]
allocated by thread 13949:

단어 'right'는 할당 끝에서 몇 바이트를 지나 잘못된 액세스가 발생했는지를 뜻합니다. 언더플로의 경우 'left'라고 표시되며 이는 할당 시작 전에 잘못된 액세스가 발생한 바이트 수를 뜻합니다.

후보 원인

SEGV_MTESERR 보고서에 다음 행이 포함되는 경우가 있습니다.

Note: multiple potential causes for this crash were detected, listing them in decreasing order of likelihood.

이는 오류 출처로 유력한 후보가 여러 개 있는데 어떤 원인이 실제 원인인지 알 수 없는 경우에 발생합니다. 사용자의 분석을 위해 실제 원인일 가능성 순으로 후보가 3개까지 표시됩니다.

signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x400007b43063db5
backtrace:
    [stack...]

Note: multiple potential causes for this crash were detected, listing them in decreasing order of probability.

Cause: [MTE]: Use After Free, 5 bytes into a 10-byte allocation at 0x7b43063db0
deallocated by thread 6663:
    [stack...]
allocated by thread 6663:
    [stack...]

Cause: [MTE]: Use After Free, 5 bytes into a 6-byte allocation at 0x7b43063db0
deallocated by thread 6663:
    [stack...]

allocated by thread 6663:
    [stack...]

위 예시는 잘못된 메모리 액세스의 의도한 대상이었을 수 있는 동일한 메모리 주소에서 감지된 두 건의 최근 할당을 보여줍니다. 이는 할당에서 사용 가능한 메모리를 재사용할 때 발생할 수 있습니다. 예를 들어 new, free, new, free, new, free, access와 같은 시퀀스가 있다면 일어날 수 있습니다. 최근 할당 기록이 먼저 나타납니다.

상세 원인 판단 휴리스틱

비정상 종료의 'Cause' 행은 액세스된 포인터의 출처인 메모리 할당을 보여주어야 합니다. 안타깝지만 MTE 하드웨어로는 일치하지 않은 태그를 포함하는 포인터를 할당으로 변환할 수 없습니다. SEGV_MTESERR 비정상 종료의 원인을 찾기 위해 Android에서 다음 데이터를 분석합니다.

  • 오류 주소(포인터 태그 포함).

  • 스택 트레이스를 포함한 힙 할당 및 메모리 태그의 최근 목록

  • 현재 활성 상태인 주변 할당과 할당의 메모리 태그

메모리 태그와 오류 주소 태그가 일치하는 오류 주소에서 최근에 할당 해제된 메모리가 'Use After Free' 오류의 원인일 수 있습니다.

메모리 태그와 오류 주소 태그가 일치하는 주변 활성 메모리가 'Buffer Overflow'(또는 'Buffer Underflow')의 원인일 수 있습니다.

시공간상으로 오류와 이웃해 있는 할당은 멀리 있는 할당보다 오류의 원인일 가능성이 큽니다.

할당 해제된 메모리는 재사용되는 경우가 많고 태그 값의 종류가 적으므로(16가지 미만) 오류 원인의 후보는 여러 개가 발견될 수 있으며 실제 원인을 자동으로 찾는 방법은 없습니다. 이러한 이유로 MTE 보고서에는 잠재적인 원인이 여러 개 표시될 수 있습니다.

앱 개발자는 오류를 일으켰을 가능성이 가장 큰 원인부터 살펴보는 것이 좋습니다. 스택 트레이스를 기준으로 하면 관련 없는 원인을 쉽게 필터링할 수 있습니다.

비동기 모드 MTE

MTE의 비동기('async') 모드에서는 SIGSEGV가 code 8(SEGV_MTEAERR) 메시지를 표시하며 다운됩니다.

SEGV_MTEAERR 오류는 프로그램이 잘못된 메모리 액세스를 실행하자마자 발생하지 않습니다. 해당 이벤트 발생 직후 문제가 감지되며 이 시점에 프로그램이 종료됩니다. 일반적으로 다음 시스템 호출 시 프로그램이 종료되지만 타이머 중단과 같이 userspace-to-kernel 전환 시점에 발생할 수도 있습니다.

SEGV_MTEAERR 오류는 메모리 주소를 저장하지 않습니다(항상 '-------'로 표시). 이 오류의 backtrace는 조건이 감지된 순간(즉, 다음 시스템 호출이나 기타 컨텍스트 전환)에 발생하며, 잘못된 액세스가 수행될 때는 발생하지 않습니다.

즉, 비동기 모드 MTE 비정상 종료 보고서의 '기본' backtrace는 일반적으로 관련성이 없습니다. 따라서 비동기 모드 오류는 동기 모드 오류보다 디버그하기 훨씬 어렵습니다. 이러한 오류는 스레드 주변 코드에서의 메모리 버그 유무를 알려주는 것으로 이해할 수 있습니다. Tombstone 파일 하단의 로그를 통해 실제 발생 상황에 관한 단서를 얻을 수 있습니다. 또는 동기 모드에서 오류를 재현하고 동기 모드에서 제공하는 상세 진단 결과를 사용하면 됩니다.

고급 주제

메모리 태깅이란 힙 할당마다 4비트(0..15) 태그 값을 임의로 할당하는 것을 말합니다. 이 값은 할당된 힙 메모리에 대응하는 특수 메타데이터 영역에 저장됩니다. malloc(), new() 연산자 등 함수에서 반환된 힙 포인터의 최상위 바이트에 동일한 값이 할당됩니다.

프로세스에서 태그 검사가 사용 설정된 경우 CPU는 포인터의 최상위 바이트를 각 메모리 액세스의 메모리 태그와 자동으로 비교합니다. 태그가 일치하지 않으면 CPU에서 오류 신호를 보내며 비정상 종료가 발생합니다.

가능한 태그 값이 제한되어 있으므로 이 방법은 확률의 영향을 받습니다. 특정 포인터가 액세스하면 안 되는 메모리 위치(예: 범위 밖, 할당 해제 후('댕글링 포인터'))는 다른 태그 값을 가지며 비정상 종료를 일으킬 가능성이 높습니다. 버그 발생을 하나도 감지하지 못할 확률은 약 7%입니다. 태그 값은 무작위로 할당되므로 다음으로 발생하는 버그를 감지할 확률은 약 93%입니다.

태그 값은 아래 강조 표시된 것처럼 오류 주소 필드와 레지스터 덤프에서 확인할 수 있습니다. 이 섹션을 통해 태그가 정상적으로 설정되어 있는지 검토할 수 있고 동일한 태그 값을 갖는 다른 주변 메모리 할당도 확인할 수 있습니다. 이러한 메모리 할당은 보고서에서 나열된 할당과 함께 오류의 원인일 수 있습니다. 이는 개발자보다는 MTE나 하위 시스템 구성요소를 구현하려는 작업자에게 도움이 될 것으로 예상됩니다.

signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x0800007ae92853a0
Cause: [MTE]: Use After Free, 0 bytes into a 32-byte allocation at 0x7ae92853a0
    x0  0000007cd94227cc  x1  0000007cd94227cc  x2  ffffffffffffffd0  x3  0000007fe81919c0
    x4  0000007fe8191a10  x5  0000000000000004  x6  0000005400000051  x7  0000008700000021
    x8  0800007ae92853a0  x9  0000000000000000  x10 0000007ae9285000  x11 0000000000000030
    x12 000000000000000d  x13 0000007cd941c858  x14 0000000000000054  x15 0000000000000000
    x16 0000007cd940c0c8  x17 0000007cd93a1030  x18 0000007cdcac6000  x19 0000007fe8191c78
    x20 0000005800eee5c4  x21 0000007fe8191c90  x22 0000000000000002  x23 0000000000000000
    x24 0000000000000000  x25 0000000000000000  x26 0000000000000000  x27 0000000000000000
    x28 0000000000000000  x29 0000007fe8191b70
    lr  0000005800eee0bc  sp  0000007fe8191b60  pc  0000005800eee0c0  pst 0000000060001000

비정상 종료 보고서에는 따로 'Memory tags' 섹션도 표시되는데, 이를 통해 오류 주소 주변의 메모리 태그 정보를 확인할 수 있습니다. 아래 예시에서는 포인터 태그 '4'가 메모리 태그 'a'와 일치하지 않습니다.

Memory tags around the fault address (0x0400007b43063db5), one tag per 16 bytes:
  0x7b43063500: 0  f  0  2  0  f  0  a  0  7  0  8  0  7  0  e
  0x7b43063600: 0  9  0  8  0  5  0  e  0  f  0  c  0  f  0  4
  0x7b43063700: 0  b  0  c  0  b  0  2  0  1  0  4  0  7  0  8
  0x7b43063800: 0  b  0  c  0  3  0  a  0  3  0  6  0  b  0  a
  0x7b43063900: 0  3  0  4  0  f  0  c  0  3  0  e  0  0  0  c
  0x7b43063a00: 0  3  0  2  0  1  0  8  0  9  0  4  0  3  0  4
  0x7b43063b00: 0  5  0  2  0  5  0  a  0  d  0  6  0  d  0  2
  0x7b43063c00: 0  3  0  e  0  f  0  a  0  0  0  0  0  0  0  4
=>0x7b43063d00: 0  0  0  a  0  0  0  e  0  d  0 [a] 0  f  0  e
  0x7b43063e00: 0  7  0  c  0  9  0  a  0  d  0  2  0  0  0  c
  0x7b43063f00: 0  0  0  6  0  b  0  8  0  3  0  0  0  5  0  e
  0x7b43064000: 0  d  0  2  0  7  0  a  0  7  0  a  0  d  0  8
  0x7b43064100: 0  b  0  2  0  b  0  4  0  1  0  6  0  d  0  4
  0x7b43064200: 0  1  0  6  0  f  0  2  0  f  0  6  0  5  0  c
  0x7b43064300: 0  1  0  4  0  d  0  6  0  f  0  e  0  1  0  8
  0x7b43064400: 0  f  0  4  0  3  0  2  0  1  0  2  0  5  0  6

모든 레지스터 값 주변의 메모리 콘텐츠를 보여주는 Tombstone 섹션에서는 태그 값도 표시됩니다.

memory near x10 ([anon:scudo:primary]):
0000007b4304a000 7e82000000008101 000003e9ce8b53a0  .......~.S......
0700007b4304a010 0000200000006001 0000000000000000  .`... ..........
0000007b4304a020 7c03000000010101 000003e97c61071e  .......|..a|....
0200007b4304a030 0c00007b4304a270 0000007ddc4fedf8  p..C{.....O.}...
0000007b4304a040 84e6000000008101 000003e906f7a9da  ................
0300007b4304a050 ffffffff00000042 0000000000000000  B...............
0000007b4304a060 8667000000010101 000003e9ea858f9e  ......g.........
0400007b4304a070 0000000100000001 0000000200000002  ................
0000007b4304a080 f5f8000000010101 000003e98a13108b  ................
0300007b4304a090 0000007dd327c420 0600007b4304a2b0   .'.}......C{...
0000007b4304a0a0 88ca000000010101 000003e93e5e5ac5  .........Z^>....
0a00007b4304a0b0 0000007dcc4bc500 0300007b7304cb10  ..K.}......s{...
0000007b4304a0c0 0f9c000000010101 000003e9e1602280  ........."`.....
0900007b4304a0d0 0000007dd327c780 0700007b7304e2d0  ..'.}......s{...
0000007b4304a0e0 0d1d000000008101 000003e906083603  .........6......
0a00007b4304a0f0 0000007dd327c3b8 0000000000000000  ..'.}.......

출처 : 바로가기

Last updated