AddressSanitizer (ASan), कंपाइलर पर आधारित एक टूल है. यह नेटिव कोड में मेमोरी से जुड़ी गड़बड़ियों का पता लगाता है.
ASan इन समस्याओं का पता लगाता है:
- स्टैक और हीप बफ़र ओवरफ़्लो/अंडरफ़्लो
- फ़्री किए गए हीप का इस्तेमाल
- स्कोप से बाहर स्टैक का इस्तेमाल किया गया
- डबल फ़्री/वाइल्ड फ़्री
ASan, 32-बिट और 64-बिट ARM के साथ-साथ x86 और x86-64 पर काम करता है. ASan का सीपीयू ओवरहेड करीब दो गुना होता है. कोड साइज़ ओवरहेड 50% से दो गुना के बीच होता है. साथ ही, इसमें मेमोरी ओवरहेड भी ज़्यादा होता है. यह आपके ऐलोकेशन पैटर्न पर निर्भर करता है, लेकिन यह दो गुना के क्रम में होता है.
Android 10 और AArch64 पर AOSP की नई रिलीज़ ब्रांच, हार्डवेयर की मदद से काम करने वाले AddressSanitizer (HWASan) के साथ काम करती है. यह एक ऐसा टूल है जिसमें कम रैम का इस्तेमाल होता है और ज़्यादा तरह के बग का पता लगाया जा सकता है. HWASan, ASan से पता लगाई गई गड़बड़ियों के अलावा, स्टैक यूज़ आफ्टर रिटर्न की गड़बड़ी का भी पता लगाता है.
HWASan में सीपीयू और कोड साइज़ का ओवरहेड एक जैसा होता है. हालांकि, इसमें रैम का ओवरहेड काफ़ी कम (15%) होता है. HWASan, नॉनडिटरमिनिस्टिक होता है. टैग की सिर्फ़ 256 संभावित वैल्यू होती हैं. इसलिए, किसी भी गड़बड़ी के छूट जाने की संभावना 0.4% होती है. HWASan में, ASan की तरह ओवरफ़्लो का पता लगाने के लिए, सीमित साइज़ वाले रेड ज़ोन नहीं होते. साथ ही, इसमें यूज़-आफ़्टर-फ़्री का पता लगाने के लिए, सीमित क्षमता वाला क्वारंटीन नहीं होता. इसलिए, HWASan के लिए यह मायने नहीं रखता कि ओवरफ़्लो कितना बड़ा है या मेमोरी को कब डीऐलोकेट किया गया था. इस वजह से, HWASan, ASan से बेहतर है. HWASan के डिज़ाइन या Android पर HWASan के इस्तेमाल के बारे में ज़्यादा जानें.
ASan, हीप ओवरफ़्लो के साथ-साथ स्टैक/ग्लोबल ओवरफ़्लो का भी पता लगाता है. साथ ही, यह कम मेमोरी ओवरहेड के साथ तेज़ी से काम करता है.
इस दस्तावेज़ में, ASan की मदद से 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
की जगह /system/lib/asan
में चली जाती है. इसके बाद, इस कमांड का इस्तेमाल करके एक्ज़ीक्यूटेबल फ़ाइल चलाएं:
LD_LIBRARY_PATH=/system/lib/asan
सिस्टम डेमॉन के लिए, /init.rc
या /init.$device$.rc
के सही सेक्शन में यह जोड़ें.
setenv LD_LIBRARY_PATH /system/lib/asan
पुष्टि करें कि प्रोसेस, /system/lib/asan
से लाइब्रेरी का इस्तेमाल कर रही है. इसके लिए, /proc/$PID/maps
पढ़ें. अगर ऐसा नहीं है, तो आपको 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
जोड़ें.
system/core/rootdir/init.zygote(32|64).rc
फ़ाइल के service zygote
सेक्शन में बदलाव करके, इन लाइनों को 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
में बदलता है. इसे ASan की मदद से बनाया गया है. यह डाइनैमिक लाइब्रेरी के खोज पाथ की शुरुआत में /system/lib/asan
भी जोड़ता है. इस तरह, asanwrapper
के साथ चलाने पर, /system/lib
में सामान्य लाइब्रेरी के बजाय /system/lib/asan
की ASan-इंस्ट्रुमेंटेड लाइब्रेरी को प्राथमिकता दी जाती है.
अगर कोई गड़बड़ी मिलती है, तो ऐप्लिकेशन बंद हो जाता है और रिपोर्ट को लॉग में प्रिंट कर दिया जाता है.
SANITIZE_TARGET
Android 7.0 और इसके बाद के वर्शन में, एक साथ ASan के साथ पूरे Android प्लैटफ़ॉर्म को बनाने की सुविधा शामिल है. (अगर आपको Android 9 से ज़्यादा वर्शन के लिए रिलीज़ बनानी है, तो HWASan एक बेहतर विकल्प है.)
एक ही बिल्ड ट्री में, यहां दी गई कमांड चलाएं.
make -j42
SANITIZE_TARGET=address make -j42
इस मोड में, userdata.img
में अतिरिक्त लाइब्रेरी होती हैं. इसलिए, इसे डिवाइस पर फ़्लैश करना भी ज़रूरी है. यह कमांड लाइन इस्तेमाल करें:
fastboot flash userdata && fastboot flashall
इससे शेयर की गई लाइब्रेरी के दो सेट बनते हैं: पहला, /system/lib
में सामान्य लाइब्रेरी (पहली बार मेक इनवोकेशन) और दूसरा, /data/asan/lib
में ASan-इंस्ट्रुमेंटेड लाइब्रेरी (दूसरी बार मेक इनवोकेशन). दूसरे बिल्ड के एक्ज़ीक्यूटेबल, पहले बिल्ड के एक्ज़ीक्यूटेबल को बदल देते हैं. ASan की सुविधा वाले एक्ज़ीक्यूटेबल के लिए, लाइब्रेरी का खोज पाथ अलग होता है. इसमें PT_INTERP
में /system/bin/linker_asan
का इस्तेमाल करके, /data/asan/lib
से पहले /system/lib
शामिल होता है.
$SANITIZE_TARGET
वैल्यू में बदलाव होने पर, बिल्ड सिस्टम इंटरमीडिएट ऑब्जेक्ट डायरेक्ट्री को मिटा देता है. यह /system/lib
में इंस्टॉल की गई बाइनरी को सुरक्षित रखते हुए, सभी टारगेट को फिर से बनाने के लिए मजबूर करता है.
एएसान की मदद से, कुछ टारगेट नहीं बनाए जा सकते:
- स्टैटिक तरीके से लिंक की गई एक्ज़ीक्यूटेबल फ़ाइलें
LOCAL_CLANG:=false
टारगेटLOCAL_SANITIZE:=false
,SANITIZE_TARGET=address
के लिए ASan'd नहीं हैं
इस तरह के एक्ज़ीक्यूटेबल को SANITIZE_TARGET
बिल्ड में शामिल नहीं किया जाता है. साथ ही, पहली बार मेक इनवोकेशन के दौरान इस्तेमाल किया गया वर्शन, /system/bin
में छोड़ दिया जाता है.
इस तरह की लाइब्रेरी, ASan के बिना बनाई जाती हैं. इनमें उन स्टैटिक लाइब्रेरी का कुछ ASan कोड शामिल हो सकता है जिन पर ये निर्भर करती हैं.