AddressSanitizer (ASan), तेज़ कंपाइलर पर आधारित एक टूल है. इसका इस्तेमाल, नेटिव कोड में मौजूद मेमोरी बग का पता लगाने के लिए किया जाता है.
ASan इनका पता लगाता है:
- स्टैक और हेप बफ़र ओवरफ़्लो/अंडरफ़्लो
- फ़्री होने के बाद हीप का इस्तेमाल
- स्टैक का बाहरी दायरा इस्तेमाल करें
- डबल फ़्री/वाइल्ड फ़्री
ASan, 32-बिट और 64-बिट ARM के साथ-साथ x86 और x86-64 पर भी काम करता है. ASan के सीपीयू पर होने वाला ओवरहेड करीब दो गुना होता है. कोड साइज़ का ओवरहेड 50% से दो गुना के बीच होता है. साथ ही, ज़्यादा मेमोरी का ओवरहेड होता है. यह आपके ऐलोकेशन पैटर्न पर निर्भर करता है, लेकिन यह दो गुना होता है.
AArch64 पर, Android 10 और AOSP की मुख्य ब्रांच जिस पर हार्डवेयर से सहायता पाने वाले AddressSanitizer (HWASan) की सुविधा काम करती हो, मिलता-जुलता टूल, जिसकी रैम कम होती है और कई तरह की गड़बड़ियों का पता चला है. HWASan, ASan से पता लगाई गई गड़बड़ियों के अलावा, रिटर्न के बाद स्टैक के इस्तेमाल का पता लगाता है.
HWASan में सीपीयू और कोड साइज़ का ओवरहेड एक जैसा है, लेकिन रैम का ओवरहेड बहुत कम (15%) है. HWASan, नॉन-डेटरमिनिस्टिक है. टैग की सिर्फ़ 256 वैल्यू हो सकती हैं. इसलिए, किसी भी गड़बड़ी को न ढूंढ पाने की संभावना 0.4% होती है. HWASan में, ASan के सीमित साइज़ वाले रेड ज़ोन नहीं होते. ये ज़ोन, ओवरफ़्लो का पता लगाने के लिए होते हैं. साथ ही, इसमें सीमित क्षमता वाले क्वॉरंटीन भी नहीं होते. ये क्वॉरंटीन, इस्तेमाल के बाद मेमोरी को डिलीकेट करने का पता लगाने के लिए होते हैं. इसलिए, HWASan के लिए यह मायने नहीं रखता कि ओवरफ़्लो कितना बड़ा है या मेमोरी को कब डिलीकेट किया गया था. इससे HWASan, ASan से बेहतर बनता है. यहां दी गई जानकारी पाने के लिए, इसकी डिज़ाइन HWASan या Android पर HWASan के इस्तेमाल के बारे में जानकारी.
ASan, हेप ओवरफ़्लो के साथ-साथ स्टैक/ग्लोबल ओवरफ़्लो का पता लगाता है. साथ ही, यह कम से कम मेमोरी ओवरहेड के साथ तेज़ी से काम करता है.
इस दस्तावेज़ में, ASan की मदद से Android के कुछ हिस्सों या पूरे Android को बनाने और चलाने का तरीका बताया गया है. अगर ASan की मदद से SDK/NDK ऐप्लिकेशन बनाया जा रहा है, तो इसके बजाय Address Sanitizer देखें.
ASan की मदद से, एक्ज़ीक्यूट किए जा सकने वाले व्यक्तिगत दस्तावेज़ों को साफ़ करें
LOCAL_SANITIZE:=address
या sanitize: { address: true }
को इसमें जोड़ें
बिल्ड नियम की शर्तों के बारे में बताता है. आप मौजूदा उदाहरणों के लिए कोड की खोज कर सकते है या
सैनिटाइज़र का इस्तेमाल नहीं कर सकते.
किसी बग का पता चलने पर, ASan
आउटपुट और logcat
तक भेजता है और फिर प्रोसेस को क्रैश कर देता है.
शेयर की गई लाइब्रेरी को ASan से साफ़ करें
ASan के काम करने के तरीके की वजह से, ASan के साथ बनाई गई लाइब्रेरी का इस्तेमाल सिर्फ़ एक्ज़ीक्यूटेबल जिसे ASan के साथ बनाया गया है.
शेयर की गई ऐसी लाइब्रेरी को सैनिटाइज़ करने के लिए जिसका इस्तेमाल कई एक्सीक्यूटेबल में किया जाता है, आपको लाइब्रेरी की दो कॉपी की ज़रूरत होती है. हालांकि, इनमें से सभी एक्सीक्यूटेबल को ASan के साथ नहीं बनाया जाता. ऐसा करने का सुझाया गया तरीका यह है कि जिस मॉड्यूल के लिए यह करना है उसके लिए Android.mk
में ये जोड़ें:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
यह लाइब्रेरी को इसके बजाय /system/lib/asan
में रखता है
/system/lib
. इसके बाद, एक्ज़ीक्यूटेबल को इसके साथ चलाएं:
LD_LIBRARY_PATH=/system/lib/asan
सिस्टम डीमन के लिए, नीचे दिए गए विकल्पों को
/init.rc
या /init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
पुष्टि करें कि प्रोसेस, /proc/$PID/maps
को पढ़कर /system/lib/asan
की लाइब्रेरी का इस्तेमाल कर रही है. अगर ऐसा नहीं है, तो आपको
SELinux को अक्षम करने के लिए:
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.
बेहतर स्टैक ट्रेस
ASan, प्रोग्राम में मेमोरी के हर ऐलोकेशन और डिऐलोकेशन इवेंट के लिए, स्टैक ट्रैस रिकॉर्ड करने के लिए, फ़्रेम-पॉइंटर पर आधारित तेज़ अनवाइंडर का इस्तेमाल करता है. Android का ज़्यादातर हिस्सा, फ़्रेम पॉइंटर के बिना बनाया गया है. इस वजह से, आपको अक्सर सिर्फ़ एक या दो सही फ़्रेम के साथ. इसे ठीक करने के लिए, लाइब्रेरी को ASan (सुझाया गया!) या इनमें से किसी एक के साथ फिर से बनाएं:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
इसके अलावा, प्रोसेस के एनवायरमेंट में ASAN_OPTIONS=fast_unwind_on_malloc=0
सेट करें. बाद वाली प्रोसेस, सीपीयू की ज़रूरत के हिसाब से हो सकती है
लोड हो सके.
प्रतीक
शुरुआत में, ASan रिपोर्ट में बाइनरी और शेयर की गई लाइब्रेरी में ऑफ़सेट के रेफ़रंस होते हैं. सोर्स फ़ाइल और लाइन की जानकारी पाने के दो तरीके हैं:
- पक्का करें कि
llvm-symbolizer
बाइनरी,/system/bin
में मौजूद हो.llvm-symbolizer
कोthird_party/llvm/tools/llvm-symbolizer
में मौजूद सोर्स से बनाया गया है. external/compiler-rt/lib/asan/scripts/symbolize.py
के ज़रिए रिपोर्ट को फ़िल्टर करें स्क्रिप्ट.
होस्ट पर सिंबल वाली लाइब्रेरी उपलब्ध होने की वजह से, दूसरे तरीके से ज़्यादा डेटा (यानी file:line
जगहें) मिल सकता है.
ऐप्लिकेशन में ASan
ASan, Java कोड को नहीं देख सकता. हालांकि, यह JNI में गड़बड़ियों का पता लगा सकता है
लाइब्रेरी. इसके लिए, आपको ASan के साथ एक्ज़ीक्यूटेबल बनाना होगा, जिसमें
यह मामला /system/bin/app_process(32|64)
का है. यह
ASan को डिवाइस पर मौजूद सभी ऐप्लिकेशन में एक साथ चालू करती है.
भारी लोड होता है, लेकिन 2 जीबी वाले डिवाइस में यह काम किया जा सकता है.
frameworks/base/cmds/app_process
में app_process
के बिल्ड नियम में LOCAL_SANITIZE:=address
जोड़ें. नज़रअंदाज़ करें
फ़िलहाल, इसी फ़ाइल में app_process__asan
टारगेट (अगर यह है)
उस समय भी वहाँ मौजूद रहेंगी).
इसके service zygote
सेक्शन में बदलाव करें
जोड़ने के लिए सही system/core/rootdir/init.zygote(32|64).rc
फ़ाइल
इंडेंट की गई लाइनों के ब्लॉक में आने वाली लाइनें, जिनमें class main
भी शामिल है
समान राशि से इंडेंट किया गया:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
बिल्ड, adb सिंक, फ़ास्टबूट फ़्लैश बूट, और फिर से चालू करें.
wrap प्रॉपर्टी का इस्तेमाल करना
पिछले सेक्शन में बताए गए तरीके से, ASan को सिस्टम में मौजूद ऐप्लिकेशन (वास्तव में, Zygote के हर वंशज में प्रोसेस). ASan की मदद से, सिर्फ़ एक या कई ऐप्लिकेशन चलाए जा सकते हैं. इसके लिए, ऐप्लिकेशन के स्टार्टअप में थोड़ी देरी हो सकती है.
ऐसा करने के लिए, अपने ऐप्लिकेशन को wrap.
प्रॉपर्टी से शुरू करें.
यहां दिए गए उदाहरण में, ASan के तहत Gmail ऐप्लिकेशन को चलाया गया है:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
इस संदर्भ में, asanwrapper
/system/bin/app_process
को फिर से लिखता है
/system/bin/asan/app_process
को, जिसे इसके साथ बनाया गया है
असैन. यह डाइनैमिक लाइब्रेरी के खोज पाथ की शुरुआत में भी /system/lib/asan
जोड़ता है. इस तरह, asanwrapper
के साथ काम करते समय, /system/lib/asan
की ASan-इंस्ट्रूमेंट की गई लाइब्रेरी को /system/lib
की सामान्य लाइब्रेरी के बजाय प्राथमिकता दी जाती है.
अगर कोई गड़बड़ी मिलती है, तो ऐप्लिकेशन क्रैश हो जाता है और रिपोर्ट को लॉग में प्रिंट कर दिया जाता है.
SANITIZE_TARGET
Android 7.0 और उसके बाद के वर्शन में, Android 7.0 और इसके बाद के वर्शन के लिए, सभी सुविधाएं मिलती हैं. एक बार में असैन. (अगर आपको Android 9 से पहले के वर्शन वाली रिलीज़ बनानी है, तो HWASan बेहतर है.)
उसी बिल्ड ट्री में इन कमांड को चलाएं.
make -j42
SANITIZE_TARGET=address make -j42
इस मोड में, userdata.img
में अतिरिक्त लाइब्रेरी होती हैं और उन्हें डिवाइस पर भी फ़्लैश करना ज़रूरी होता है. नीचे दी गई कमांड लाइन का इस्तेमाल करें:
fastboot flash userdata && fastboot flashall
यह शेयर की गई लाइब्रेरी के दो सेट बनाता है: सामान्य तौर पर
/system/lib
(पहली बार शुरू करने के लिए) और ASan
/data/asan/lib
(दूसरे नंबर पर प्रॉम्प्ट देना). एक्ज़ीक्यूटेबल
दूसरा बिल्ड पहले बिल्ड से ओवरराइट हो जाता है. असैन-इंस्ट्रूमेंटेड
एक्ज़ीक्यूटेबल को अलग लाइब्रेरी का खोज पाथ मिलता है जिसमें
इस आइटम का इस्तेमाल करने पर, /system/lib
से पहले /data/asan/lib
मिलेंगे
PT_INTERP
में /system/bin/linker_asan
.
$SANITIZE_TARGET
वैल्यू बदलने पर, बिल्ड सिस्टम इंटरमीडिएट ऑब्जेक्ट डायरेक्ट्री को मिटा देता है. इससे /system/lib
में इंस्टॉल की गई बाइनरी को सुरक्षित रखते हुए, सभी टारगेट को फिर से बनाया जाता है.
कुछ टारगेट, ASan की मदद से नहीं बनाए जा सकते:
- स्टैटिक रूप से लिंक किए गए एक्ज़ीक्यूटेबल
LOCAL_CLANG:=false
टारगेटSANITIZE_TARGET=address
के लिएLOCAL_SANITIZE:=false
, ASan's नहीं हैं
इस तरह के एक्सीक्यूटेबल, SANITIZE_TARGET
बिल्ड में शामिल नहीं किए जाते. साथ ही, पहले make अनुरोध का वर्शन /system/bin
में ही छोड़ दिया जाता है.
इस तरह की लाइब्रेरी ASan के बिना बनाई गई हैं. उसमें ASan के साथ कुछ किया जा सकता है से कोड कॉपी किया जा सकता है, जिस पर वे निर्भर करती हैं.