AddressSanitizer (ASan), yerel koddaki bellek hatalarını algılamak için derleyici tabanlı hızlı bir araçtır.
ASan şunları algılar:
- Yığın ve yığın tamponu taşması/yetersizliği
- Ücretsiz kullanımdan sonra yığın kullanımı
- Kapsam dışında yığın kullanımı
- Çift serbest/vahşi serbest
ASan hem 32 bit hem de 64 bit ARM'de, ayrıca x86 ve x86-64'te çalışır. ASan'ın CPU yükü yaklaşık 2 kat, kod boyutu yükü% 50 ila 2 kat arasında ve bellek yükü büyüktür (ayrılma kalıplarınıza bağlıdır ancak 2 kat civarındadır).
Android 10 ve AArch64'teki AOSP ana dalı, daha düşük RAM yükü ve daha geniş bir tespit edilen hata yelpazesine sahip benzer bir araç olan Donanım Destekli AddressSanitizer (HWASan)'ı destekler. HWASan, ASan tarafından algılanan hataların yanı sıra döndürülen yığın kullanımını da algılar.
HWASan, benzer CPU ve kod boyutu yükü, ancak çok daha küçük bir RAM yükü (%15) sunar. HWASan, kesin olmayan bir yöntemdir. Yalnızca 256 olası etiket değeri vardır.Bu nedenle, herhangi bir hatayı kaçırma ihtimali %0,4'tür. HWASan, taşmayı algılamak için ASan'ın sınırlı boyutlu kırmızı bölgelerine ve serbest bırakıldıktan sonra kullanmayı algılamak için sınırlı kapasiteli karantinaya sahip değildir. Bu nedenle, taşmanın ne kadar büyük olduğu veya belleğin ne kadar süre önce tahsis edildiği HWASan için önemli değildir. Bu sayede HWASan, ASan'dan daha iyidir. HWASan'ın tasarımı veya Android'de HWASan'ın kullanımı hakkında daha fazla bilgi edinebilirsiniz.
ASan, yığın taşmalarına ek olarak yığın/genel taşmaları da algılar ve minimum bellek ek yükü ile hızlıdır.
Bu belgede, Android'in bir kısmının/tümünün ASan ile nasıl derlenip çalıştırılacağı açıklanmaktadır. ASan ile SDK/NDK uygulaması oluşturuyorsanız bunun yerine Address Sanitizer başlıklı makaleyi inceleyin.
Bağımsız yürütülebilir dosyaları ASan ile temizleyin
Yürütülebilir dosyanın derleme kuralına LOCAL_SANITIZE:=address
veya sanitize: { address: true }
ekleyin. Mevcut örnekler için kodda arama yapabilir veya mevcut diğer temizleyicileri bulabilirsiniz.
ASan, bir hata algıladığında hem standart çıkışa hem de logcat
'e ayrıntılı bir rapor yazdırır ve ardından işlemi kilitler.
Paylaşılan kitaplıkları ASan ile temizleyin
ASan'ın çalışma şekli nedeniyle, ASan ile oluşturulan bir kitaplık yalnızca ASan ile oluşturulmuş yürütülebilir dosyalar tarafından kullanılabilir.
Hepsi ASan ile oluşturulmamış olan birden çok yürütülebilir dosyada kullanılan paylaşılan bir kitaplığı temizlemek için kitaplığın iki kopyasına ihtiyacınız vardır. Bunu yapmanın önerilen yolu, söz konusu modül için Android.mk
alanına aşağıdakileri eklemektir:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Bu işlem, kitaplığı /system/lib
yerine /system/lib/asan
içine yerleştirir. Ardından, yürütülebilir dosyanızı şununla çalıştırın:
LD_LIBRARY_PATH=/system/lib/asan
Sistem daemon'ları için /init.rc
veya /init.$device$.rc
'un uygun bölümüne aşağıdakileri ekleyin.
setenv LD_LIBRARY_PATH /system/lib/asan
İşlemin, /proc/$PID/maps
dosyasını okuyarak /system/lib/asan
kitaplıklarını kullandığından emin olun. Aksi halde SELinux'u devre dışı bırakmanız gerekebilir:
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.
Daha iyi yığın izlemeler
ASan, programdaki her bellek ayırma ve bellek ayırma etkinliği için yığın izleme kaydetmek üzere hızlı, çerçeve işaretçisine dayalı bir çözücü kullanır. Android'in çoğu, kare işaretçileri olmadan tasarlanmıştır. Sonuç olarak, genellikle yalnızca bir veya iki anlamlı kare elde edersiniz. Bu sorunu düzeltmek için kitaplığı ASan ile (önerilir) veya aşağıdakilerle yeniden oluşturun:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Alternatif olarak, işlem ortamında ASAN_OPTIONS=fast_unwind_on_malloc=0
değerini ayarlayabilirsiniz. Yüke bağlı olarak ikincisi çok fazla CPU kullanabilir.
Simgeleştirme
ASan raporları başlangıçta ikili dosyalardaki ve paylaşılan kitaplıklardaki ofsetlere referanslar içerir. Kaynak dosya ve satır bilgilerini iki şekilde edinebilirsiniz:
llvm-symbolizer
ikili dosyası/system/bin
içinde bulunduğundan emin olun.llvm-symbolizer
,third_party/llvm/tools/llvm-symbolizer
içindeki kaynaklardan derlenir.- Raporu
external/compiler-rt/lib/asan/scripts/symbolize.py
komut dosyasıyla filtreleyin.
İkinci yaklaşım, ana makinede sembolize edilmiş kitaplıkların bulunması nedeniyle daha fazla veri (yani file:line
konum) sağlayabilir.
Uygulamalarda ASan
ASan, Java kodunu göremez ancak JNI kitaplıklarındaki hataları algılayabilir. Bunun için yürütülebilir dosyayı ASan ile derlemeniz gerekir. Bu durumda /system/bin/app_process(32|64)
kullanılır. Bu, ASan'ı cihazdaki tüm uygulamalarda aynı anda etkinleştirir. Bu, ağır bir yüktür ancak 2 GB RAM'e sahip bir cihaz bunu kaldırabilir.
frameworks/base/cmds/app_process
'deki app_process
derleme kuralına LOCAL_SANITIZE:=address
ekleyin. Şu anda aynı dosyadaki app_process__asan
hedefini yoksayın (bunu okuduğunuz sırada hâlâ oradaysa).
İlgili system/core/rootdir/init.zygote(32|64).rc
dosyasının service zygote
bölümünü düzenleyerek class main
içeren girintili satır bloğuna aşağıdaki satırları ekleyin. Bu satırlar da aynı miktarda girintili olmalıdır:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Derleme, adb senkronizasyonu, fastboot flash boot ve yeniden başlatma.
wrap mülkünü kullanma
Önceki bölümdeki yaklaşım, ASan'ı sistemdeki her uygulamaya (aslında Zygote sürecinin her alt öğesine) yerleştirir. ASan ile yalnızca bir (veya birkaç) uygulama çalıştırmak mümkündür. Bu da, daha yavaş uygulama başlatma için ek bellek yükünden yararlanır.
Bunu, uygulamanızı wrap.
mülküyle başlatarak yapabilirsiniz.
Aşağıdaki örnekte, Gmail uygulaması ASan altında çalıştırılmaktadır:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
Bu bağlamda asanwrapper
, /system/bin/app_process
öğesini ASan ile oluşturulan /system/bin/asan/app_process
olarak yeniden yazar. Ayrıca dinamik kitaplık arama yolunun başına /system/lib/asan
ekler. Bu sayede, asanwrapper
ile çalıştırıldığında /system/lib/asan
'teki ASan enstrümante edilmiş kitaplıklar, /system/lib
'teki normal kitaplıklara tercih edilir.
Bir hata bulunursa uygulama kilitlenir ve rapor günlüke yazdırılır.
SANITIZE_TARGET
Android 7.0 ve sonraki sürümler, Android platformunun tamamını bir kerede ASan ile derleme desteği içerir. (Android 9'dan daha yeni bir sürüm oluşturuyorsanız HWASan daha iyi bir seçenektir.)
Aynı derleme ağacında aşağıdaki komutları çalıştırın.
make -j42
SANITIZE_TARGET=address make -j42
Bu modda userdata.img
ek kitaplıklar içerir ve cihaza da yüklenmelidir. Aşağıdaki komut satırını kullanın:
fastboot flash userdata && fastboot flashall
Bu işlem, iki ortak kitaplık grubu oluşturur: /system/lib
içinde normal (ilk make çağrısı) ve /data/asan/lib
içinde ASan enstrümante edilmiş (ikinci make çağrısı). İkinci derlemeden gelen yürütülebilir dosyalar, ilk derlemeden gelenlerin üzerine yazılır. Santigratlı yürütülebilir dosyalar, PT_INTERP
öğesinde /system/bin/linker_asan
kullanımı aracılığıyla /system/lib
öncesindeki /data/asan/lib
içeren farklı bir kitaplık arama yoluna sahip olur.
Derleme sistemi, $SANITIZE_TARGET
değeri değiştiğinde ara nesne dizinlerini siler. Bu işlem, /system/lib
altındaki yüklü ikili programları korurken tüm hedeflerin yeniden oluşturulmasını zorunlu kılar.
Bazı hedefler ASan ile oluşturulamaz:
- Statik olarak bağlanmış yürütülebilir dosyalar
LOCAL_CLANG:=false
hedefLOCAL_SANITIZE:=false
,SANITIZE_TARGET=address
için ASan'd değil
Bu tür yürütülebilir dosyalar SANITIZE_TARGET
derlemesinde atlanır ve ilk make çağrısından gelen sürüm /system/bin
içinde bırakılır.
Bu tür kitaplıklar ASan olmadan oluşturulur. Bunlar, bağlı oldukları statik kitaplıklardaki bazı ASan kodlarını içerebilir.