कंट्रोल फ़्लो इंटिग्रिटी

साल 2016 तक, Android में मौजूद सभी कमज़ोरियों में से करीब 86% कमज़ोरियां, मेमोरी सेफ़्टी से जुड़ी थीं. ज़्यादातर कमज़ोरियों का फ़ायदा हमलावर उठाते हैं. इसके लिए, वे किसी ऐप्लिकेशन के सामान्य कंट्रोल फ़्लो में बदलाव करते हैं, ताकि ऐप्लिकेशन के सभी विशेषाधिकारों का इस्तेमाल करके, नुकसान पहुंचाने वाली गतिविधियां की जा सकें. कंट्रोल फ़्लो इंटिग्रिटी (सीएफ़आई) एक सुरक्षा सुविधा है. यह कंपाइल की गई बाइनरी के ओरिजनल कंट्रोल फ़्लो ग्राफ़ में बदलाव करने की अनुमति नहीं देती. इससे इस तरह के हमलों को अंजाम देना काफ़ी मुश्किल हो जाता है.

Android 8.1 में, हमने मीडिया स्टैक में LLVM के CFI को लागू किया है. Android 9 में, हमने ज़्यादा कॉम्पोनेंट और कर्नल में सीएफ़आई की सुविधा चालू की है. सिस्टम CFI डिफ़ॉल्ट रूप से चालू होता है. हालांकि, आपको कर्नल CFI को चालू करना होगा.

LLVM के CFI के लिए, लिंक-टाइम ऑप्टिमाइज़ेशन (एलटीओ) का इस्तेमाल करके कंपाइल करना ज़रूरी है. एलटीओ, ऑब्जेक्ट फ़ाइलों के LLVM बिटकोड को लिंक-टाइम तक सेव रखता है. इससे कंपाइलर को यह बेहतर तरीके से पता चलता है कि कौनसे ऑप्टिमाइज़ेशन किए जा सकते हैं. एलटीओ चालू करने से, फ़ाइनल बाइनरी का साइज़ कम हो जाता है और परफ़ॉर्मेंस बेहतर होती है. हालांकि, कंपाइल होने में ज़्यादा समय लगता है. Android पर टेस्टिंग के दौरान, LTO और CFI के कॉम्बिनेशन से कोड साइज़ और परफ़ॉर्मेंस पर बहुत कम असर पड़ता है. कुछ मामलों में, दोनों में सुधार हुआ है.

सीएफ़आई और फ़ॉरवर्ड-कंट्रोल की अन्य जांचों को कैसे हैंडल किया जाता है, इस बारे में ज़्यादा तकनीकी जानकारी के लिए, 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 डिफ़ॉल्ट रूप से चालू होता है. CFI, Android का इस्तेमाल करने वाले लोगों को सुरक्षित रखने में मदद करता है. इसलिए, आपको इसे बंद नहीं करना चाहिए.

हमारा सुझाव है कि आप अन्य कॉम्पोनेंट के लिए भी सीएफ़आई की सुविधा चालू करें. खास अधिकारों वाले नेटिव कोड या ऐसे नेटिव कोड के लिए यह तरीका सबसे सही है जो उपयोगकर्ता का इनपुट प्रोसेस करता है जिस पर भरोसा नहीं किया जा सकता. अगर clang और Android बिल्ड सिस्टम का इस्तेमाल किया जा रहा है, तो नई कॉम्पोनेंट में सीएफ़आई को चालू किया जा सकता है. इसके लिए, आपको अपनी मेकफ़ाइल या ब्लूप्रिंट फ़ाइलों में कुछ लाइनें जोड़नी होंगी.

मेकफ़ाइलों में सीएफ़आई की सुविधा काम करती है

/platform/frameworks/av/cmds/stagefright/Android.mk जैसी मेक फ़ाइल में सीएफ़आई चालू करने के लिए, यह जोड़ें:

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
  • LOCAL_SANITIZE से पता चलता है कि बिल्ड के दौरान, सैनिटाइज़र के तौर पर CFI का इस्तेमाल किया गया है.
  • LOCAL_SANITIZE_DIAG, सीएफ़आई के लिए डाइग्नोस्टिक मोड चालू करता है. गड़बड़ी का पता लगाने वाले मोड में, क्रैश के दौरान 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 इंस्ट्रूमेंटेशन को बंद कर दे.

Android का बिल्ड सिस्टम, हर कॉम्पोनेंट के लिए ब्लैकलिस्ट करने की सुविधा देता है. इससे आपको ऐसे सोर्स फ़ाइल या फ़ंक्शन चुनने की अनुमति मिलती है जिनमें सीएफ़आई इंस्ट्रूमेंटेशन नहीं होगा. यह सुविधा, Make और Soong, दोनों के लिए उपलब्ध है. ब्लैकलिस्ट फ़ाइल के फ़ॉर्मैट के बारे में ज़्यादा जानकारी के लिए, अपस्ट्रीम Clang के दस्तावेज़ देखें.

सत्यापन

फ़िलहाल, CFI के लिए कोई खास सीटीएस टेस्ट उपलब्ध नहीं है. इसके बजाय, यह पक्का करें कि सीएफ़आई की सुविधा चालू होने या न होने पर, सीटीएस टेस्ट पास हो जाएं. इससे यह पुष्टि की जा सकेगी कि सीएफ़आई की सुविधा से डिवाइस पर कोई असर नहीं पड़ रहा है.