साल 2016 तक, Android में मौजूद सभी कमज़ोरियों में से करीब 86% मेमोरी सेफ़्टी से जुड़ी थीं. ज़्यादातर कमज़ोरियों का फ़ायदा हमलावर उठाते हैं. इसके लिए, वे किसी ऐप्लिकेशन के सामान्य कंट्रोल फ़्लो में बदलाव करते हैं, ताकि ऐप्लिकेशन के सभी विशेषाधिकारों का इस्तेमाल करके, नुकसान पहुंचाने वाली गतिविधियां की जा सकें. कंट्रोल फ़्लो इंटिग्रिटी (सीएफ़आई) एक सुरक्षा सुविधा है. यह कंपाइल की गई बाइनरी के ओरिजनल कंट्रोल फ़्लो ग्राफ़ में बदलाव करने की अनुमति नहीं देती. इससे इस तरह के हमलों को अंजाम देना काफ़ी मुश्किल हो जाता है.
Android 8.1 में, हमने मीडिया स्टैक में सीएफ़आई को लागू करने के लिए एलएलवीएम को चालू किया है. Android 9 में, हमने ज़्यादा कॉम्पोनेंट और कर्नल में CFI की सुविधा चालू की है. सिस्टम CFI डिफ़ॉल्ट रूप से चालू होता है. हालांकि, आपको कर्नल CFI को चालू करना होगा.
LLVM के CFI के लिए, लिंक-टाइम ऑप्टिमाइज़ेशन (एलटीओ) का इस्तेमाल करके कंपाइल करना ज़रूरी है. एलटीओ, ऑब्जेक्ट फ़ाइलों के LLVM बिटकोड को लिंक-टाइम तक सेव रखता है. इससे कंपाइलर को यह बेहतर तरीके से समझने में मदद मिलती है कि कौनसे ऑप्टिमाइज़ेशन किए जा सकते हैं. एलटीओ चालू करने से, फ़ाइनल बाइनरी का साइज़ कम हो जाता है और परफ़ॉर्मेंस बेहतर होती है. हालांकि, कंपाइल होने में लगने वाला समय बढ़ जाता है. Android पर टेस्टिंग के दौरान, एलटीओ और सीएफ़आई के कॉम्बिनेशन से कोड के साइज़ और परफ़ॉर्मेंस पर बहुत कम असर पड़ता है. कुछ मामलों में, दोनों में सुधार हुआ है.
सीएफ़आई और फ़ॉरवर्ड-कंट्रोल की अन्य जांचों को कैसे हैंडल किया जाता है, इस बारे में ज़्यादा तकनीकी जानकारी के लिए, LLVM डिज़ाइन का दस्तावेज़ देखें.
उदाहरण और सोर्स
सीएफ़आई की सुविधा कंपाइलर देता है. यह कंपाइल टाइम के दौरान, बाइनरी में इंस्ट्रुमेंटेशन जोड़ता है. हम AOSP में Clang टूलचेन और Android बिल्ड सिस्टम में सीएफ़आई का इस्तेमाल करते हैं.
CFI, Arm64 डिवाइसों के लिए डिफ़ॉल्ट रूप से चालू होता है. यह /platform/build/target/product/cfi-common.mk
में मौजूद कॉम्पोनेंट के सेट के लिए होता है.
यह सीधे तौर पर, मीडिया कॉम्पोनेंट के मेकफ़ाइल/ब्लूप्रिंट फ़ाइलों के सेट में भी चालू होता है. जैसे, /platform/frameworks/av/media/libmedia/Android.bp
और /platform/frameworks/av/cmds/stagefright/Android.mk
.
सिस्टम सीएफ़आई लागू करना
Clang और Android बिल्ड सिस्टम का इस्तेमाल करने पर, CFI डिफ़ॉल्ट रूप से चालू होता है. सीएफ़आई, Android का इस्तेमाल करने वाले लोगों को सुरक्षित रखने में मदद करता है. इसलिए, आपको इसे बंद नहीं करना चाहिए.
हमारा सुझाव है कि आप अन्य कॉम्पोनेंट के लिए भी सीएफ़आई की सुविधा चालू करें. इसके लिए, ऐसे नेटिव कोड सबसे सही होते हैं जिन्हें खास अधिकार मिले हों या जो ऐसे नेटिव कोड हों जो किसी ऐसे उपयोगकर्ता के इनपुट को प्रोसेस करते हों जिस पर भरोसा नहीं किया जा सकता. अगर clang और Android बिल्ड सिस्टम का इस्तेमाल किया जा रहा है, तो नई कॉम्पोनेंट में सीएफ़आई को चालू किया जा सकता है. इसके लिए, आपको अपनी मेकफ़ाइल या ब्लूप्रिंट फ़ाइलों में कुछ लाइनें जोड़नी होंगी.
मेकफ़ाइलों में CFI के साथ काम करता है
/platform/frameworks/av/cmds/stagefright/Android.mk
जैसी मेक फ़ाइल में सीएफ़आई चालू करने के लिए, यह जोड़ें:
LOCAL_SANITIZE := cfi # Optional features LOCAL_SANITIZE_DIAG := cfi LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
LOCAL_SANITIZE
से पता चलता है कि बिल्ड के दौरान सैनिटाइज़र के तौर पर सीएफ़आई का इस्तेमाल किया गया है.LOCAL_SANITIZE_DIAG
CFI के लिए डायग्नोस्टिक मोड चालू करता है. गड़बड़ी का पता लगाने वाले मोड में, क्रैश के दौरान logcat में डीबग करने से जुड़ी अतिरिक्त जानकारी प्रिंट होती है. यह जानकारी, बिल्ड डेवलप और टेस्ट करते समय काम आती है. हालांकि, प्रोडक्शन बिल्ड पर गड़बड़ी की जानकारी देने वाले मोड को हटाना न भूलें.LOCAL_SANITIZE_BLACKLIST
की मदद से, कॉम्पोनेंट अलग-अलग फ़ंक्शन या सोर्स फ़ाइलों के लिए, सीएफ़आई इंस्ट्रुमेंटेशन को चुनिंदा तौर पर बंद कर सकते हैं. ब्लैकलिस्ट का इस्तेमाल, उपयोगकर्ता से जुड़ी उन समस्याओं को ठीक करने के लिए किया जा सकता है जो किसी और तरीके से ठीक नहीं हो सकतीं. ज़्यादा जानकारी के लिए, सीएफ़आई बंद करना लेख पढ़ें.
ब्लूप्रिंट फ़ाइलों में सीएफ़आई के साथ काम करने की सुविधा
/platform/frameworks/av/media/libmedia/Android.bp
जैसी ब्लूप्रिंट फ़ाइल में सीएफ़आई चालू करने के लिए, यह जोड़ें:
sanitize: { cfi: true, diag: { cfi: true, }, blacklist: "cfi_blacklist.txt", },
समस्या का हल
अगर नए कॉम्पोनेंट में सीएफ़आई चालू किया जा रहा है, तो आपको फ़ंक्शन टाइप के मेल न खाने की गड़बड़ियों और असेंबली कोड टाइप के मेल न खाने की गड़बड़ियों से जुड़ी कुछ समस्याएं आ सकती हैं.
फ़ंक्शन टाइप मैच न होने की गड़बड़ियां इसलिए होती हैं, क्योंकि CFI, इनडायरेक्ट कॉल को सिर्फ़ उन फ़ंक्शन पर जाने की अनुमति देता है जिनका डाइनैमिक टाइप, कॉल में इस्तेमाल किए गए स्टैटिक टाइप के जैसा हो. सीएफ़आई, वर्चुअल और नॉन-वर्चुअल मेंबर फ़ंक्शन कॉल को सिर्फ़ उन ऑब्जेक्ट पर जाने से रोकता है जो कॉल करने के लिए इस्तेमाल किए गए ऑब्जेक्ट के स्टैटिक टाइप की डिराइव की गई क्लास हैं. इसका मतलब है कि अगर आपके पास ऐसा कोड है जो इनमें से किसी भी मान्यता का उल्लंघन करता है, तो CFI की ओर से जोड़ा गया इंस्ट्रुमेंटेशन बंद हो जाएगा. उदाहरण के लिए, स्टैक ट्रेस में SIGABRT दिखता है और logcat में कंट्रोल फ़्लो इंटिग्रिटी के बारे में एक लाइन होती है, जिसमें बेमेल होने की जानकारी मिलती है.
इसे ठीक करने के लिए, पक्का करें कि कॉल किए गए फ़ंक्शन का टाइप वही हो जिसे स्टैटिक तौर पर घोषित किया गया था. यहां दो उदाहरण सीएल दिए गए हैं:
एक और समस्या यह हो सकती है कि आपने ऐसे कोड में CFI को चालू करने की कोशिश की हो जिसमें असेंबली को इनडायरेक्ट कॉल शामिल हों. असेंबली कोड टाइप नहीं किया गया है. इसलिए, टाइप मेल नहीं खाता.
इस समस्या को ठीक करने के लिए, हर असेंबली कॉल के लिए नेटिव कोड रैपर बनाएं. साथ ही, रैपर को कॉलिंग पॉइंटर के तौर पर एक ही फ़ंक्शन सिग्नेचर दें. इसके बाद, रैपर सीधे तौर पर असेंबली कोड को कॉल कर सकता है. डायरेक्ट ब्रांच को CFI से इंस्ट्रुमेंट नहीं किया जाता. इसलिए, उन्हें रनटाइम पर फिर से पॉइंट नहीं किया जा सकता और वे सुरक्षा के लिए खतरा नहीं होतीं. इससे समस्या ठीक हो जाएगी.
अगर असेंबली फ़ंक्शन बहुत ज़्यादा हैं और उन सभी को ठीक नहीं किया जा सकता, तो उन सभी फ़ंक्शन को भी काली सूची में डाला जा सकता है जिनमें असेंबली को इनडायरेक्ट कॉल शामिल हैं. हालांकि, इसके इस्तेमाल का सुझाव नहीं दिया जाता है. ऐसा इसलिए, क्योंकि यह इन फ़ंक्शन पर सीएफ़आई की जांच को बंद कर देता है. इससे हमला होने की आशंका बढ़ जाती है.
सीएफ़आई की सुविधा बंद करना
हमें परफ़ॉर्मेंस में कोई खास बदलाव नहीं दिखा. इसलिए, आपको सीएफ़आई बंद करने की ज़रूरत नहीं है. हालांकि, अगर उपयोगकर्ता पर इसका असर पड़ता है, तो कंपाइल करने के समय सैनिटाइज़र की ब्लैकलिस्ट वाली फ़ाइल देकर, अलग-अलग फ़ंक्शन या सोर्स फ़ाइलों के लिए CFI को चुनिंदा तौर पर बंद किया जा सकता है. ब्लैकलिस्ट, कंपाइलर को यह निर्देश देती है कि वह तय की गई जगहों पर CFI इंस्ट्रूमेंटेशन को बंद कर दे.
Android का बिल्ड सिस्टम, हर कॉम्पोनेंट के लिए ब्लैकलिस्ट करने की सुविधा देता है. इससे, आपको ऐसे सोर्स फ़ाइल या फ़ंक्शन चुनने की अनुमति मिलती है जिनमें CFI इंस्ट्रूमेंटेशन नहीं होगा. यह सुविधा, Make और Soong, दोनों के लिए उपलब्ध है. ब्लैकलिस्ट फ़ाइल के फ़ॉर्मैट के बारे में ज़्यादा जानकारी के लिए, अपस्ट्रीम Clang के दस्तावेज़ देखें.
Validation
फ़िलहाल, सीएफ़आई के लिए कोई सीटीएस टेस्ट उपलब्ध नहीं है. इसके बजाय, यह पक्का करें कि सीएफ़आई की सुविधा चालू होने या न होने पर, सीटीएस टेस्ट पास हो जाएं. इससे यह पुष्टि की जा सकेगी कि सीएफ़आई की सुविधा से डिवाइस पर कोई असर नहीं पड़ रहा है.