অ্যাড্রেসস্যানিটাইজার (ASan) হলো নেটিভ কোডের মেমরি বাগ শনাক্ত করার একটি দ্রুত কম্পাইলার-ভিত্তিক টুল।
ASan শনাক্ত করে:
- স্ট্যাক এবং হিপ বাফার ওভারফ্লো/আন্ডারফ্লো
- বিনামূল্যে ব্যবহারের পর প্রচুর ব্যবহার।
- স্কোপের বাইরে স্ট্যাক ব্যবহার
- ডাবল ফ্রি/ওয়াইল্ড ফ্রি
ASan ৩২-বিট ও ৬৪-বিট ARM, সেইসাথে x86 এবং x86-64-এ চলে। ASan-এর সিপিইউ ওভারহেড প্রায় ২x, কোড সাইজ ওভারহেড ৫০% থেকে ২x-এর মধ্যে এবং একটি বড় মেমোরি ওভারহেড রয়েছে (যা আপনার অ্যালোকেশন প্যাটার্নের উপর নির্ভরশীল, তবে প্রায় ২x-এর কাছাকাছি)।
অ্যান্ড্রয়েড ১০ এবং AArch64-এ AOSP-এর সর্বশেষ রিলিজ ব্রাঞ্চ হার্ডওয়্যার-অ্যাসিস্টেড অ্যাড্রেসস্যানিটাইজার (HWASan) সমর্থন করে, যা একটি অনুরূপ টুল হলেও এর র্যাম ওভারহেড কম এবং এটি আরও বেশি সংখ্যক বাগ শনাক্ত করতে পারে। ASan দ্বারা শনাক্ত করা বাগগুলোর পাশাপাশি, HWASan রিটার্নের পরে স্ট্যাক ব্যবহারও শনাক্ত করে।
HWASan-এর সিপিইউ এবং কোড সাইজ ওভারহেড প্রায় একই, কিন্তু এর র্যাম ওভারহেড অনেক কম (১৫%)। HWASan একটি নন-ডিটারমিনিস্টিক পদ্ধতি। এখানে মাত্র ২৫৬টি সম্ভাব্য ট্যাগ ভ্যালু রয়েছে, তাই যেকোনো বাগ শনাক্ত করতে না পারার একটি নির্দিষ্ট ০.৪% সম্ভাবনা থাকে। ওভারফ্লো শনাক্ত করার জন্য ASan-এর মতো সীমিত আকারের রেড জোন এবং ইউজ-আফটার-ফ্রি শনাক্ত করার জন্য সীমিত ধারণক্ষমতার কোয়ারেন্টাইন HWASan-এ নেই, তাই ওভারফ্লো কতটা বড় বা কতক্ষণ আগে মেমোরি ডিঅ্যালোকেট করা হয়েছিল, তা HWASan-এর কাছে কোনো বিষয় নয়। এই কারণে HWASan, ASan-এর চেয়ে উন্নত। আপনি HWASan-এর ডিজাইন বা অ্যান্ড্রয়েডে এর ব্যবহার সম্পর্কে আরও পড়তে পারেন।
ASan হিপ ওভারফ্লোর পাশাপাশি স্ট্যাক/গ্লোবাল ওভারফ্লোও শনাক্ত করে এবং এটি ন্যূনতম মেমোরি ওভারহেড সহ দ্রুত কাজ করে।
এই ডকুমেন্টটিতে বর্ণনা করা হয়েছে কিভাবে ASan ব্যবহার করে অ্যান্ড্রয়েডের অংশবিশেষ বা সম্পূর্ণ অ্যান্ড্রয়েড বিল্ড এবং রান করতে হয়। আপনি যদি 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 /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 প্রোগ্রামের প্রতিটি মেমরি অ্যালোকেশন এবং ডিঅ্যালোকেশন ইভেন্টের জন্য একটি স্ট্যাক ট্রেস রেকর্ড করতে একটি দ্রুত, ফ্রেম-পয়েন্টার-ভিত্তিক আনওয়াইন্ডার ব্যবহার করে। অ্যান্ড্রয়েডের বেশিরভাগই ফ্রেম পয়েন্টার ছাড়া তৈরি। ফলে, আপনি প্রায়শই কেবল এক বা দুটি অর্থপূর্ণ ফ্রেম পান। এটি ঠিক করতে, হয় ASan দিয়ে লাইব্রেরিটি রি-বিল্ড করুন (এটিই সুপারিশ করা হয়!), অথবা নিম্নলিখিত কমান্ডটি ব্যবহার করুন:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
অথবা প্রসেস এনভায়রনমেন্টে ASAN_OPTIONS=fast_unwind_on_malloc=0 সেট করুন। লোডের উপর নির্ভর করে, শেষেরটি খুব বেশি সিপিইউ-নিবিড় হতে পারে।
প্রতীকীকরণ
প্রাথমিকভাবে, ASan রিপোর্টগুলিতে বাইনারি এবং শেয়ার্ড লাইব্রেরির অফসেটের রেফারেন্স থাকে। সোর্স ফাইল এবং লাইনের তথ্য পাওয়ার দুটি উপায় রয়েছে:
- নিশ্চিত করুন যে
/system/binএllvm-symbolizerবাইনারিটি উপস্থিত আছে।llvm-symbolizerthird_party/llvm/tools/llvm-symbolizerসোর্স থেকে বিল্ড করা হয়। -
external/compiler-rt/lib/asan/scripts/symbolize.pyস্ক্রিপ্টটির মাধ্যমে রিপোর্টটি ফিল্টার করুন।
দ্বিতীয় পদ্ধতিটি আরও বেশি ডেটা (অর্থাৎ, file:line অবস্থান) প্রদান করতে পারে, কারণ হোস্টে সিম্বলাইজড লাইব্রেরিগুলো উপলব্ধ থাকে।
অ্যাপে ASan
ASan জাভা কোডের ভেতরে দেখতে পারে না, কিন্তু এটি JNI লাইব্রেরির বাগ শনাক্ত করতে পারে। এর জন্য, আপনাকে ASan দিয়ে এক্সিকিউটেবলটি বিল্ড করতে হবে, যা এই ক্ষেত্রে হলো /system/bin/app_process( 32|64 ) । এটি ডিভাইসের সমস্ত অ্যাপে একই সাথে ASan সক্রিয় করে, যা একটি ভারী বোঝা, কিন্তু ২ জিবি র্যামের একটি ডিভাইস এটি সামলাতে সক্ষম হওয়া উচিত।
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 sync করুন, fastboot flash boot করুন এবং রিবুট করুন।
র্যাপ প্রপার্টি ব্যবহার করুন
পূর্ববর্তী বিভাগে বর্ণিত পদ্ধতিটি সিস্টেমের প্রতিটি অ্যাপে (আসলে, জাইগোট প্রসেসের প্রতিটি উত্তরসূরিতে) ASan স্থাপন করে। ASan ব্যবহার করে শুধুমাত্র একটি (বা কয়েকটি) অ্যাপ চালানো সম্ভব, তবে এর জন্য কিছুটা মেমরি ওভারহেডের বিনিময়ে অ্যাপটি চালু হতে বেশি সময় লাগে।
এটি আপনার অ্যাপটি wrap. প্রপার্টি দিয়ে শুরু করার মাধ্যমে করা যেতে পারে। নিম্নলিখিত উদাহরণটি ASan-এর অধীনে Gmail অ্যাপটি চালায়:
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 /system/lib /system/lib/asan থেকে ASan-ইনস্ট্রুমেন্টেড লাইব্রেরিগুলোকে অগ্রাধিকার দেওয়া হয়।
যদি কোনো বাগ খুঁজে পাওয়া যায়, তাহলে অ্যাপটি ক্র্যাশ করে এবং রিপোর্টটি লগে প্রিন্ট করা হয়।
SANITIZE_TARGET
অ্যান্ড্রয়েড ৭.০ এবং এর পরবর্তী সংস্করণগুলোতে ASan ব্যবহার করে সম্পূর্ণ অ্যান্ড্রয়েড প্ল্যাটফর্মটি একবারে বিল্ড করার সুবিধা রয়েছে। (যদি আপনি অ্যান্ড্রয়েড ৯-এর চেয়ে উচ্চতর কোনো রিলিজ বিল্ড করেন, তবে HWASan একটি ভালো বিকল্প।)
একই বিল্ড ট্রিতে নিম্নলিখিত কমান্ডগুলো চালান।
make -j42SANITIZE_TARGET=address make -j42
এই মোডে, userdata.img এ অতিরিক্ত লাইব্রেরি থাকে এবং এটিও ডিভাইসে ফ্ল্যাশ করতে হবে। নিম্নলিখিত কমান্ড লাইনটি ব্যবহার করুন:
fastboot flash userdata && fastboot flashall
এটি দুই সেট শেয়ার্ড লাইব্রেরি তৈরি করে: একটি সাধারণ সেট /system/lib (প্রথম make কমান্ডের মাধ্যমে), এবং অন্যটি ASan-instrumented সেট /data/asan/lib (দ্বিতীয় make কমান্ডের মাধ্যমে)। দ্বিতীয় বিল্ড থেকে তৈরি এক্সিকিউটেবলগুলো প্রথম বিল্ডের এক্সিকিউটেবলগুলোকে ওভাররাইট করে। ASan-instrumented এক্সিকিউটেবলগুলো একটি ভিন্ন লাইব্রেরি সার্চ পাথ পায়, যেখানে PT_INTERP এ /system/bin/linker_asan ব্যবহারের মাধ্যমে / /system/lib এর আগে /data/asan/lib অন্তর্ভুক্ত থাকে।
$SANITIZE_TARGET মান পরিবর্তিত হলে বিল্ড সিস্টেম মধ্যবর্তী অবজেক্ট ডিরেক্টরিগুলো মুছে ফেলে। এর ফলে /system/lib এর অধীনে ইনস্টল করা বাইনারিগুলো অক্ষুণ্ণ রেখে সমস্ত টার্গেট পুনরায় বিল্ড করতে বাধ্য করা হয়।
কিছু লক্ষ্যবস্তু ASan দিয়ে তৈরি করা যায় না:
- স্ট্যাটিক্যালি লিঙ্ক করা এক্সিকিউটেবল
-
LOCAL_CLANG:=falseটার্গেট -
LOCAL_SANITIZE:=falseSANITIZE_TARGET=addressজন্য ASan হিসেবে গণ্য হয় না।
SANITIZE_TARGET বিল্ডে এই ধরনের এক্সিকিউটেবল ফাইলগুলো বাদ দেওয়া হয় এবং প্রথম make কমান্ডের সংস্করণটি /system/bin এ রেখে দেওয়া হয়।
এই ধরনের লাইব্রেরিগুলো ASan ছাড়াই তৈরি করা হয়। এগুলোতে তাদের নির্ভরশীল স্ট্যাটিক লাইব্রেরিগুলো থেকে কিছু ASan কোড থাকতে পারে।