Mengapa MTE?
Bug keamanan memori, yang merupakan error dalam menangani memori dalam bahasa pemrograman native, adalah masalah kode yang umum. Hal ini menyebabkan kerentanan keamanan serta masalah stabilitas.
Armv9 memperkenalkan Arm Memory Tagging Extension (MTE), ekstensi hardware yang memungkinkan Anda menangkap bug use-after-free dan buffer-overflow dalam kode native.
Memeriksa dukungan
Mulai dari Android 13, perangkat tertentu memiliki dukungan untuk MTE. Untuk memeriksa apakah perangkat Anda berjalan dengan MTE yang diaktifkan, jalankan perintah berikut:
adb shell grep mte /proc/cpuinfo
Jika hasilnya adalah Features : [...] mte
, berarti perangkat Anda berjalan dengan MTE
yang diaktifkan.
Beberapa perangkat tidak mengaktifkan MTE secara default, tetapi mengizinkan developer memulai ulang dengan MTE yang diaktifkan. Ini adalah konfigurasi eksperimental yang tidak direkomendasikan untuk penggunaan normal karena dapat menurunkan performa atau stabilitas perangkat, tetapi dapat berguna untuk pengembangan aplikasi. Untuk mengakses mode ini, buka Opsi Developer > Memory Tagging Extension di Aplikasi Setelan. Jika opsi ini tidak ada, berarti perangkat tidak mendukung pengaktifan MTE dengan cara ini.
Mode operasi MTE
MTE mendukung dua mode: SYNC dan ASYNC. Mode SYNC memberikan informasi diagnostik yang lebih baik sehingga lebih cocok untuk tujuan pengembangan, sedangkan mode ASYNC memiliki performa tinggi yang memungkinkannya diaktifkan untuk aplikasi yang dirilis.
Mode sinkron (SYNC)
Mode ini dioptimalkan untuk kemampuan debug atas performa dan dapat digunakan sebagai alat deteksi bug yang presisi, saat overhead performa yang lebih tinggi dapat diterima. Jika diaktifkan, MTE SYNC juga berfungsi sebagai mitigasi keamanan.
Jika ada ketidakcocokan tag, prosesor akan menghentikan proses pada pemuatan yang melakukan pelanggaran atau menyimpan petunjuk dengan SIGSEGV (dengan si_code SEGV_MTESERR) dan informasi lengkap tentang akses memori dan alamat faulting.
Mode ini berguna selama pengujian sebagai alternatif yang lebih cepat untuk HWASan yang tidak mengharuskan Anda mengompilasi ulang kode, atau dalam produksi, saat aplikasi Anda merepresentasikan kerentanan platform terhadap serangan. Selain itu, jika mode ASYNC (dijelaskan di bawah) telah menemukan bug, laporan bug yang akurat dapat diperoleh dengan menggunakan API runtime untuk mengalihkan eksekusi ke mode SYNC.
Selain itu, saat berjalan dalam mode SYNC, pengalokasi Android akan merekam stack trace setiap alokasi dan dealokasi serta menggunakannya untuk memberikan laporan error yang lebih baik yang menyertakan penjelasan tentang error memori, seperti use-after-free atau buffer-overflow, dan stack trace peristiwa memori yang relevan (lihat Memahami laporan MTE untuk mengetahui detail selengkapnya). Laporan tersebut memberikan informasi yang lebih kontekstual dan membuat bug lebih mudah dilacak dan diperbaiki daripada dalam mode ASYNC.
Mode asinkron (ASYNC)
Mode ini dioptimalkan untuk performa atas akurasi laporan bug dan dapat digunakan untuk mendeteksi overhead rendah dari bug keamanan memori. Jika ada ketidakcocokan tag, pemroses akan melanjutkan eksekusi hingga entri kernel terdekat (seperti syscall atau interupsi timer), yang menghentikan proses dengan SIGSEGV (kode SEGV_MTEAERR) tanpa merekam alamat faulting atau akses memori.
Mode ini berguna untuk memitigasi kerentanan keamanan memori dalam produksi pada codebase yang telah diuji dengan baik, dengan kepadatan bug keamanan memori yang diketahui rendah, yang dicapai dengan menggunakan mode SYNC selama pengujian.
Mengaktifkan MTE
Untuk satu perangkat
Untuk eksperimen, perubahan kompatibilitas aplikasi dapat digunakan untuk menetapkan nilai default
atribut memtagMode
untuk aplikasi yang tidak menentukan
nilai apa pun dalam manifes (atau menentukan "default"
).
Fitur ini dapat ditemukan di bagian Sistem > Lanjutan > Opsi developer > Perubahan
Kompatibilitas Aplikasi di menu setelan global. Menetapkan NATIVE_MEMTAG_ASYNC
atau NATIVE_MEMTAG_SYNC
akan mengaktifkan MTE untuk aplikasi tertentu.
Anda juga dapat menyetelnya menggunakan perintah am
sebagai berikut:
- Untuk mode SYNC:
$ adb shell am compat enable NATIVE_MEMTAG_SYNC my.app.name
- Untuk mode ASYNC:
$ adb shell am compat enable NATIVE_MEMTAG_ASYNC my.app.name
Di Gradle
Anda dapat mengaktifkan MTE untuk semua build debug project Gradle dengan menempatkan
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application android:memtagMode="sync" tools:replace="android:memtagMode"/>
</manifest>
ke dalam app/src/debug/AndroidManifest.xml
. Tindakan ini akan mengganti memtagMode
manifes Anda dengan sinkronisasi untuk build debug.
Atau, Anda dapat mengaktifkan MTE untuk semua build buildType kustom. Untuk
melakukannya, buat buildType Anda sendiri dan masukkan
XML ke dalam app/src/<name of buildType>/AndroidManifest.xml
.
Untuk APK di perangkat apa pun yang mendukung
MTE dinonaktifkan secara default. Aplikasi yang ingin menggunakan MTE dapat
melakukannya dengan menyetel android:memtagMode
pada tag <application>
atau <process>
di AndroidManifest.xml
.
android:memtagMode=(off|default|sync|async)
Jika ditetapkan pada tag <application>
, atribut tersebut akan memengaruhi semua proses yang digunakan
oleh aplikasi, dan dapat diganti untuk setiap proses dengan menetapkan
tag <process>
.
Membangun aplikasi dengan instrumentasi
Mengaktifkan MTE seperti yang dijelaskan sebelumnya membantu mendeteksi bug kerusakan memori pada heap native. Untuk mendeteksi kerusakan memori pada tumpukan, selain mengaktifkan MTE untuk aplikasi, kode perlu dibuat ulang dengan instrumentasi. Tujuan aplikasi yang dihasilkan hanya akan berjalan di perangkat yang mendukung MTE.
Untuk membangun kode native (JNI) aplikasi dengan MTE, lakukan hal berikut:
ndk-build
Dalam file Application.mk
Anda:
APP_CFLAGS := -fsanitize=memtag -fno-omit-frame-pointer -march=armv8-a+memtag
APP_LDFLAGS := -fsanitize=memtag -fsanitize-memtag-mode=sync -march=armv8-a+memtag
CMake
Untuk setiap target dalam CMakeLists.txt:
target_compile_options(${TARGET} PUBLIC -fsanitize=memtag -fno-omit-frame-pointer -march=armv8-a+memtag)
target_link_options(${TARGET} PUBLIC -fsanitize=memtag -fsanitize-memtag-mode=sync -march=armv8-a+memtag)
Menjalankan aplikasi
Setelah mengaktifkan MTE, gunakan dan uji aplikasi Anda seperti biasa. Jika masalah keamanan memori
terdeteksi, aplikasi Anda akan mengalami error dengan tombstone yang terlihat seperti ini (perhatikan
SIGSEGV
dengan SEGV_MTESERR
untuk SYNC atau SEGV_MTEAERR
untuk ASYNC):
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)
Learn more about MTE reports: https://source.android.com/docs/security/test/memory-safety/mte-report
Lihat Memahami laporan MTE dalam dokumentasi AOSP untuk mengetahui detail selengkapnya. Anda juga dapat men-debug aplikasi dengan Android Studio dan debugger berhenti pada baris yang menyebabkan akses memori tidak valid.
Pengguna Lanjutan: Menggunakan MTE di alokator Anda sendiri
Agar dapat menggunakan MTE untuk memori yang tidak dialokasikan melalui alokator sistem normal, Anda harus mengubah alokator untuk memberi tag pada memori dan pointer.
Halaman untuk pengalokasi Anda harus dialokasikan menggunakan PROT_MTE
dalam
tanda prot
dari mmap
(atau mprotect
).
Semua alokasi yang diberi tag harus diselaraskan dengan 16 byte, karena tag hanya dapat ditetapkan untuk potongan 16 byte (juga dikenal sebagai granule).
Kemudian, sebelum menampilkan pointer, Anda perlu menggunakan petunjuk IRG
untuk
membuat tag acak dan menyimpannya di pointer.
Gunakan petunjuk berikut untuk memberi tag pada memori yang mendasari:
STG
: memberi tag pada satu granule 16 byteST2G
: memberi tag pada dua granule 16 byteDC GVA
: cacheline tag dengan tag yang sama
Atau, petunjuk berikut juga melakukan inisialisasi nol pada memori:
STZG
: memberi tag dan melakukan inisialisasi nol pada satu granule 16 byteSTZ2G
: memberi tag dan melakukan inisialisasi nol pada dua granule 16 byteDC GZVA
: memberi tag dan melakukan inisialisasi nol pada cacheline dengan tag yang sama
Perhatikan bahwa petunjuk ini tidak didukung di CPU lama, sehingga Anda perlu menjalankannya secara bersyarat saat MTE diaktifkan. Anda dapat memeriksa apakah MTE diaktifkan untuk proses Anda:
#include <sys/prctl.h>
bool runningWithMte() {
int mode = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
return mode != -1 && mode & PR_MTE_TCF_MASK;
}
Penerapan scudo mungkin akan bermanfaat sebagai referensi.
Pelajari lebih lanjut
Anda dapat mempelajari lebih lanjut di Panduan Pengguna MTE untuk Android OS yang ditulis oleh Arm.