AddressSanitizer

AddressSanitizer (ASan), yerel kodda bellek hatalarını tespit etmek için kullanılan hızlı bir derleyici tabanlı araçtır.

ASan'ın algıladığı sorunlar:

  • Yığın ve bellek alanı arabellek taşması/taşma altı
  • Heap use after free
  • Yığın kullanımının kapsamı dışında
  • İki katı ücretsiz/sınırsız ücretsiz

ASan, 32 bit ve 64 bit ARM'nin yanı sıra x86 ve x86-64'te çalışır. ASan'ın CPU ek yükü yaklaşık 2 kat, kod boyutu ek yükü% 50 ile 2 kat arasında ve büyük bir bellek ek yükü (ayrım kalıplarınıza bağlı olarak 2 kat civarında) vardır.

Android 10 ve AArch64'teki AOSP'nin en son yayın dalı, donanım destekli AddressSanitizer (HWASan)'ı destekler. Bu araç, daha düşük RAM ek yüküne ve daha geniş bir hata tespit aralığına sahip benzer bir araçtır. HWASan, ASan tarafından tespit edilen hatalara ek olarak geri dönüşten sonra yığın kullanımını da tespit eder.

HWASan'ın CPU ve kod boyutu ek yükü benzerdir ancak RAM ek yükü çok daha küçüktür (%15). HWASan deterministik değildir. Yalnızca 256 olası etiket değeri vardır.Bu nedenle, herhangi bir hatanın gözden kaçma olasılığı %0,4'tür. HWASan'da, taşmaları tespit etmek için ASan'ın sınırlı boyutlu kırmızı bölgeleri ve serbest bırakıldıktan sonra kullanmayı tespit etmek için sınırlı kapasiteli karantina özelliği yoktur. Bu nedenle, taşmanın boyutu veya belleğin ne kadar süre önce serbest bırakıldığı HWASan için önemli değildir. Bu özellik, HWASan'ı ASan'dan daha iyi hale getirir. HWASan'ın tasarımı veya Android'de HWASan kullanımı hakkında daha fazla bilgi edinebilirsiniz.

ASan, yığın taşmalarına ek olarak yığın/global taşmaları da algılar ve minimum bellek yüküyle hızlıdır.

Bu belgede, Android'in bir bölümünün veya tamamının ASan ile nasıl oluşturulup çalıştırılacağı açıklanmaktadır. ASan ile SDK/NDK uygulaması oluşturuyorsanız bunun yerine Address Sanitizer başlıklı makaleyi inceleyin.

ASan ile ayrı ayrı yürütülebilir dosyaları temizleme

Yürütülebilir dosya için derleme kuralına LOCAL_SANITIZE:=address veya sanitize: { address: true } ekleyin. Mevcut örnekler için veya diğer kullanılabilir temizleyicileri bulmak üzere kodda arama yapabilirsiniz.

Bir hata algılandığında ASan, hem standart çıkışa hem de logcat'ya ayrıntılı bir rapor yazdırır ve ardından işlemi kilitler.

Paylaşılan kitaplıkları ASan ile temizleme

ASan'ın çalışma şekli nedeniyle, ASan ile oluşturulan bir kitaplık yalnızca ASan ile oluşturulan bir yürütülebilir dosya tarafından kullanılabilir.

Birden fazla yürütülebilir dosyada kullanılan ve tamamı ASan ile oluşturulmamış bir paylaşılan 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 öğesine aşağıdakileri eklemektir:

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

Bu işlem, kitaplığı /system/lib yerine /system/lib/asan konumuna yerleştirir. Ardından, yürütülebilir dosyanızı şu komutla çalıştırın:

LD_LIBRARY_PATH=/system/lib/asan

Sistem daemon'ları için /init.rc veya /init.$device$.rc'nin uygun bölümüne aşağıdakileri ekleyin.

setenv LD_LIBRARY_PATH /system/lib/asan

İşlemin, /system/lib/asan kitaplıklarını kullandığını doğrulayın./proc/$PID/maps bölümünü okuyarak bu kitaplıkların mevcut olup olmadığını kontrol edin. Etkin değilse 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 izleri

ASan, programdaki her bellek ayırma ve bellekten çıkarma etkinliği için yığın izi kaydetmek üzere hızlı, çerçeve işaretçisine dayalı bir sarmalayıcı kullanır. Android'in çoğu, çerçeve işaretçileri olmadan oluşturulur. Bu nedenle, 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, ASAN_OPTIONS=fast_unwind_on_malloc=0 cihazını süreç ortamında ayarlayın. İkincisi, yüke bağlı olarak CPU'yu çok fazla kullanabilir.

Simgeselleştirme

Başlangıçta ASan raporları, ikili dosyalardaki ve paylaşılan kitaplıklardaki ofsetlere referanslar içerir. Kaynak dosya ve satır bilgilerini edinmenin iki yolu vardır:

  • llvm-symbolizer ikilisinin /system/bin içinde bulunduğundan emin olun. llvm-symbolizer, third_party/llvm/tools/llvm-symbolizer kaynaklarından oluşturulmuştur.
  • Raporu external/compiler-rt/lib/asan/scripts/symbolize.py komut dosyası aracılığıyla filtreleyin.

İkinci yaklaşım, ana makinede sembolize edilmiş kitaplıkların kullanılabilirliği nedeniyle daha fazla veri (yani file:line konum) sağlayabilir.

ASan'lar (uygulama içi)

ASan, Java kodunu göremez ancak JNI kitaplıklarındaki hataları algılayabilir. Bunun için, bu örnekte /system/bin/app_process(32|64) olan ASan ile yürütülebilir dosya oluşturmanız gerekir. Bu, cihazdaki tüm uygulamalarda ASan'ı aynı anda etkinleştirir. Bu durum ağır bir yük olsa da 2 GB RAM'e sahip bir cihaz bunu kaldırabilir.

LOCAL_SANITIZE:=address öğesini frameworks/base/cmds/app_process içindeki app_process derleme kuralına ekleyin.

Uygun 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 sync, fastboot flash boot ve yeniden başlatma.

Wrap özelliğini kullanma

Önceki bölümdeki yaklaşım, ASan'ı sistemdeki her uygulamaya (aslında Zygote sürecinin her alt sürecine) yerleştirir. ASan ile yalnızca bir (veya birkaç) uygulamayı çalıştırmak mümkündür. Bu durumda, uygulama başlangıcı yavaşlar ancak bellek ek yükü azalır.

Bu işlem, uygulamanızı wrap. özelliğiyle başlatarak yapılabilir. 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, ASan ile oluşturulan /system/bin/asan/app_process olarak yeniden yazılır. /system/bin/app_process Ayrıca, dinamik kitaplık arama yolunun başına /system/lib/asan ekler. Bu şekilde, asanwrapper ile çalışırken /system/lib/asan içindeki ASan ile donatılmış kitaplıklar, /system/lib içindeki normal kitaplıklara tercih edilir.

Bir hata bulunursa uygulama kilitlenir ve rapor, günlük dosyasına yazdırılır.

SANITIZE_TARGET

Android 7.0 ve sonraki sürümler, Android platformunun tamamının ASan ile tek seferde oluşturulmasını destekler. (Android 9'dan daha yüksek bir sürüm oluşturuyorsanız HWASan daha iyi bir seçimdir.)

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 grup paylaşılan kitaplık oluşturur: /system/lib içinde normal (ilk derleme çağrısı) ve /data/asan/lib içinde ASan ile donatılmış (ikinci derleme çağrısı). İkinci derlemedeki yürütülebilir dosyalar, birinci derlemedekilerin üzerine yazılır. ASan ile donatılmış yürütülebilir dosyalar, /data/asan/lib ile /system/lib arasında PT_INTERP içinde /system/bin/linker_asan kullanılarak farklı bir kitaplık arama yolu alır.

Derleme sistemi, $SANITIZE_TARGET değeri değiştiğinde ara nesne dizinlerini siler. Bu, /system/lib altında yüklü ikili dosyaları korurken tüm hedeflerin yeniden oluşturulmasını zorlar.

Bazı hedefler ASan ile oluşturulamaz:

  • Statik olarak bağlanmış yürütülebilir dosyalar
  • LOCAL_CLANG:=false hedef
  • LOCAL_SANITIZE:=false, SANITIZE_TARGET=address için ASan'd değil

Bunun gibi 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. Bağlı oldukları statik kitaplıklardan bazı ASan kodları içerebilirler.

Destekleyici belgeler