AddressSanitizer (ASan), नेटिव कोड में मेमोरी से जुड़ी गड़बड़ियों का पता लगाने के लिए, कंपाइलर पर आधारित एक तेज़ टूल है.
ASan इन गड़बड़ियों का पता लगाता है:
- स्टैक और हीप बफ़र ओवरफ़्लो/अंडरफ़्लो
- फ़्री की गई मेमोरी को इस्तेमाल करने की गड़बड़ी
- स्कोप से बाहर स्टैक का इस्तेमाल
- जल्दी-जल्दी दो बार फ़्री करना/अनियंत्रित फ़्री करना
ASan, 32-बिट और 64-बिट ARM के साथ-साथ x86 और x86-64 पर भी काम करता है. ASan का सीपीयू ओवरहेड करीब-करीब 2x होता है. कोड साइज़ ओवरहेड 50% से 2x के बीच होता है. साथ ही, इसमें मेमोरी का ज़्यादा ओवरहेड होता है. यह आपके ऐलोकेशन पैटर्न पर निर्भर करता है, लेकिन यह करीब-करीब 2x होता है.
Android 10 और AArch64 पर AOSP के सबसे नए रिलीज़ ब्रांच में, हार्डवेयर-असिस्टेड AddressSanitizer (HWASan) की सुविधा उपलब्ध है. यह भी ASan की तरह ही एक टूल है. इसमें रैम का ओवरहेड कम होता है और यह ज़्यादा गड़बड़ियों का पता लगा सकता है. ASan से पता लगने वाली गड़बड़ियों के अलावा, HWASan, स्टैक के इस्तेमाल के बाद वापस आने की गड़बड़ी का भी पता लगाता है.
HWASan का सीपीयू और कोड साइज़ ओवरहेड, ASan के जैसा ही होता है. हालांकि, इसमें रैम का ओवरहेड बहुत कम (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 के बजाय /system/lib/asan में सेव हो जाती है. इसके बाद, अपने एक्ज़ीक्यूटेबल को इस कमांड के साथ चलाएं:
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 rootadb 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 सिंक करें, फ़ास्टबूट फ़्लैश बूट करें, और रीबूट करें.
रैप प्रॉपर्टी का इस्तेमाल करना
पिछले सेक्शन में बताया गया तरीका, सिस्टम में मौजूद हर ऐप्लिकेशन में ASan को शामिल करता है. असल में, यह Zygote प्रोसेस के हर डिसेंडेंट में ASan को शामिल करता है. सिर्फ़ एक (या कुछ) ऐप्लिकेशन को ASan की मदद से चलाया जा सकता है. इसके लिए, ऐप्लिकेशन के स्टार्टअप में लगने वाले समय को कम करने के लिए, मेमोरी ओवरहेड को बढ़ाया जा सकता है.
अपने ऐप्लिकेशन को wrap. प्रॉपर्टी के साथ शुरू करके, ऐसा किया जा सकता है.
यहां दिए गए उदाहरण में, Gmail ऐप्लिकेशन को ASan के तहत चलाने का तरीका बताया गया है:
adb rootadb shell setenforce 0 # disable SELinuxadb 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/asan में मौजूद ASan-इंस्ट्रूमेंटेड
लाइब्रेरी को /system/lib में मौजूद सामान्य लाइब्रेरी
के मुकाबले प्राथमिकता दी जाती है.
अगर कोई गड़बड़ी मिलती है, तो ऐप्लिकेशन क्रैश हो जाता है. साथ ही, रिपोर्ट को लॉग में प्रिंट किया जाता है.
SANITIZE_TARGET
Android 7.0 और इसके बाद के वर्शन में, पूरे Android प्लैटफ़ॉर्म को एक साथ ASan की मदद से बनाने की सुविधा शामिल है. (अगर Android 9 से ज़्यादा का वर्शन बनाया जा रहा है, तो HWASan एक बेहतर विकल्प है.)
एक ही बिल्ड ट्री में ये कमांड चलाएं.
make -j42SANITIZE_TARGET=address make -j42
इस मोड में, userdata.img में अतिरिक्त लाइब्रेरी शामिल होती हैं. इसलिए, इसे डिवाइस पर भी फ़्लैश करना ज़रूरी है. यह कमांड लाइन इस्तेमाल करें:
fastboot flash userdata && fastboot flashall
इससे शेयर की गई लाइब्रेरी के दो सेट बनते हैं: /system/lib में सामान्य लाइब्रेरी (पहली बार मेक कमांड चलाने पर) और /data/asan/lib में ASan-इंस्ट्रूमेंटेड लाइब्रेरी (दूसरी बार मेक कमांड चलाने पर). दूसरे बिल्ड के एक्ज़ीक्यूटेबल, पहले बिल्ड के एक्ज़ीक्यूटेबल को बदल देते हैं. ASan-इंस्ट्रूमेंटेड एक्ज़ीक्यूटेबल को लाइब्रेरी का एक अलग खोज पाथ मिलता है. इसमें PT_INTERP में /system/bin/linker_asan का इस्तेमाल करके, /system/lib से पहले /data/asan/lib शामिल होता है.
$SANITIZE_TARGET की वैल्यू बदलने पर, बिल्ड सिस्टम, इंटरमीडिएट ऑब्जेक्ट डायरेक्ट्री को मिटा देता है. इससे /system/lib में इंस्टॉल की गई बाइनरी को बनाए रखते हुए, सभी टारगेट को फिर से बनाना पड़ता है.
कुछ टारगेट को ASan की मदद से नहीं बनाया जा सकता:
- स्टैटिक तौर पर लिंक किए गए एक्ज़ीक्यूटेबल
LOCAL_CLANG:=falseटारगेटLOCAL_SANITIZE:=falseकोSANITIZE_TARGET=addressके लिए ASan की मदद से नहीं बनाया जाता
SANITIZE_TARGET बिल्ड में, इस तरह के एक्ज़ीक्यूटेबल को छोड़ दिया जाता है. साथ ही, पहली बार मेक कमांड चलाने पर बना वर्शन, /system/bin में सेव रहता है.
इस तरह की लाइब्रेरी को ASan की मदद से नहीं बनाया जाता. इनमें, उन स्टैटिक लाइब्रेरी से कुछ ASan कोड शामिल हो सकता है जिन पर ये निर्भर करती हैं.