AArch64 सिस्टम बाइनरी के लिए, एक्ज़ीक्यूटेबल कोड सेक्शन को डिफ़ॉल्ट रूप से सिर्फ़-एक्ज़ीक्यूट (नहीं पढ़ा जा सकता) के तौर पर मार्क किया जाता है. ऐसा, सिर्फ़-समय पर कोड का फिर से इस्तेमाल करने से जुड़े हमलों को रोकने के लिए किया जाता है. डेटा और कोड को एक साथ मिलाने वाला कोड और ऐसे कोड जो जान-बूझकर इन सेक्शन की जांच करते हैं (पहले मेमोरी सेगमेंट को पढ़ने लायक के तौर पर फिर से मैप किए बिना) अब काम नहीं करते. अगर कोई ऐप्लिकेशन, सिर्फ़ एक बार इस्तेमाल होने वाली मेमोरी (XOM) की सुविधा वाली सिस्टम लाइब्रेरी के कोड सेक्शन को पढ़ने की कोशिश करता है, तो उस पर असर पड़ता है. ऐसा तब होता है, जब ऐप्लिकेशन पहले सेक्शन को पढ़ने लायक के तौर पर मार्क किए बिना, मेमोरी में मौजूद उस सेक्शन को पढ़ने की कोशिश करता है. यह असर, टारगेट SDK टूल के तौर पर 10 (एपीआई लेवल 29 या इसके बाद के वर्शन) का इस्तेमाल करने वाले ऐप्लिकेशन पर पड़ता है.
इस समस्या को कम करने का पूरा फ़ायदा पाने के लिए, हार्डवेयर और कर्नेल, दोनों के साथ काम करने वाले डिवाइस की ज़रूरत होती है. इस सहायता के बिना, हो सकता है कि समस्या को कम करने के लिए किए गए उपाय का सिर्फ़ कुछ हिस्सा लागू हो. Android 4.9 के सामान्य कर्नेल में, ARMv8.2 डिवाइसों पर इस सुविधा के लिए पूरी मदद देने के लिए सही पैच शामिल हैं.
लागू करना
कंपाइलर से जनरेट की गई AArch64 बाइनरी, यह मानती हैं कि कोड और डेटा को एक साथ इस्तेमाल नहीं किया गया है. इस सुविधा को चालू करने से, डिवाइस की परफ़ॉर्मेंस पर बुरा असर नहीं पड़ता.
ऐसे कोड के लिए जिसे अपने रन किए जा सकने वाले सेगमेंट पर, जान-बूझकर मेमोरी की जांच करनी है, हमारा सुझाव है कि कोड के उन सेगमेंट पर mprotect
को कॉल करें जिनकी जांच की ज़रूरत है, ताकि उन्हें पढ़ा जा सके. इसके बाद, जांच पूरी होने पर, पढ़ने की सुविधा हटा दें.
इस तरीके से लागू करने पर, सिर्फ़ 'कार्रवाई करें' के तौर पर मार्क किए गए मेमोरी सेगमेंट में पढ़ने की वजह से, सेगमेंटेशन फ़ॉल्ट (SEGFAULT
) होता है. ऐसा किसी गड़बड़ी, जोखिम, कोड (लिटरल पूलिंग) के साथ डेटा के मिलने या मेमोरी की जांच करने की वजह से हो सकता है.
डिवाइस से जुड़ी सहायता और असर
हो सकता है कि ज़रूरी पैच के बिना, पुराने हार्डवेयर या पुराने कर्नेल (4.9 से पहले के) वाले डिवाइसों पर, यह सुविधा पूरी तरह काम न करे या इसका फ़ायदा न मिल पाए. जिन डिवाइसों में कर्नेल की सुविधा नहीं है वे शायद सिर्फ़ एक्सीक्यूट की जा सकने वाली मेमोरी को उपयोगकर्ता के ऐक्सेस के लिए लागू न कर पाएं. हालांकि, कर्नेल कोड, जो साफ़ तौर पर यह जांच करता है कि पेज को पढ़ा जा सकता है या नहीं, वह अब भी इस प्रॉपर्टी को लागू कर सकता है, जैसे कि process_vm_readv()
.
यह पक्का करने के लिए कि कर्नेल, सिर्फ़ चलाने के लिए मार्क किए गए यूज़रलैंड पेजों का सम्मान करता है, कर्नेल में कर्नेल फ़्लैग CONFIG_ARM64_UAO
सेट होना चाहिए. ऐसा हो सकता है कि ARMv8 के पुराने डिवाइसों या उपयोगकर्ता ऐक्सेस बदलने (UAO) की सुविधा बंद किए गए ARMv8.2 डिवाइसों को इससे पूरा फ़ायदा न मिले. हालांकि, ये डिवाइस अब भी सिस्टम कॉल का इस्तेमाल करके, सिर्फ़ 'चालू करें' पेजों को पढ़ सकते हैं.
मौजूदा कोड को फिर से बनाना
AArch32 से पोर्ट किए गए कोड में, डेटा और कोड, दोनों शामिल हो सकते हैं. इस वजह से, समस्याएं आ सकती हैं. ज़्यादातर मामलों में, इन समस्याओं को ठीक करना उतना ही आसान होता है जितना कि कॉन्स्टेंट को असेंबली फ़ाइल के .data
सेक्शन में ले जाना.
स्थानीय तौर पर पूल किए गए कॉन्स्टेंट को अलग करने के लिए, हो सकता है कि हाथ से लिखी गई असेंबली को फिर से लिखना पड़े.
उदाहरण:
Clang कंपाइलर से जनरेट की गई बाइनरी में, कोड में डेटा के इंटरमिक्स होने से जुड़ी कोई समस्या नहीं होनी चाहिए. अगर स्टैटिक लाइब्रेरी से GNU कंपाइलर कलेक्शन (GCC) का जनरेट किया गया कोड शामिल किया गया है, तो आउटपुट बाइनरी की जांच करें और पक्का करें कि कॉन्स्टेंट को कोड सेक्शन में पूल न किया गया हो.
अगर कोड के ऐसे सेक्शन पर कोड की जांच करना ज़रूरी है जिन्हें चलाया जा सकता है, तो कोड को पढ़ने लायक के तौर पर मार्क करने के लिए, सबसे पहले mprotect
को कॉल करें. इसके बाद, ऑपरेशन पूरा होने पर, mprotect
को फिर से कॉल करके, उसे पढ़े न जा सकने वाले के तौर पर मार्क करें.
XOM चालू करना
बिल्ड सिस्टम में मौजूद सभी 64-बिट बाइनरी के लिए, सिर्फ़-इंस्टॉल करने की सुविधा डिफ़ॉल्ट रूप से चालू होती है.
XOM बंद करना
मॉड्यूल लेवल पर, पूरी सबडायरेक्ट्री ट्री या पूरे बिल्ड के लिए, सिर्फ़-इंस्टॉल करने की सुविधा को बंद किया जा सकता है.
LOCAL_XOM
और xom
वैरिएबल को false
पर सेट करके, उन अलग-अलग मॉड्यूल के लिए XOM को बंद किया जा सकता है जिन्हें फिर से तैयार नहीं किया जा सकता या जिनका रन होने वाला कोड पढ़ना ज़रूरी है.
// Android.mk LOCAL_XOM := false // Android.bp cc_binary { // or other module types ... xom: false, }
अगर किसी स्टैटिक लाइब्रेरी में सिर्फ़ एक्ज़ीक्यूट करने के लिए मेमोरी बंद है, तो बिल्ड सिस्टम इसे उस स्टैटिक लाइब्रेरी के सभी मॉड्यूल पर लागू करता है. xom: true,
का इस्तेमाल करके, इसे बदला जा सकता है.
किसी खास सबडायरेक्ट्री (उदाहरण के लिए,
foo/bar/) में, सिर्फ़ एक बार इस्तेमाल होने वाली मेमोरी को बंद करने के लिए, XOM_EXCLUDE_PATHS
को वैल्यू पास करें.
make -j XOM_EXCLUDE_PATHS=foo/bar
इसके अलावा, अपने प्रॉडक्ट कॉन्फ़िगरेशन में PRODUCT_XOM_EXCLUDE_PATHS
वैरिएबल सेट किया जा सकता है.
सिर्फ़ चलाए जा सकने वाले बाइनरी को दुनिया भर में बंद किया जा सकता है. इसके लिए, अपने make
कमांड में ENABLE_XOM=false
डालें.
make -j ENABLE_XOM=false
पुष्टि करें
सिर्फ़ एक्सीक्यूट करने के लिए उपलब्ध मेमोरी के लिए, सीटीएस या पुष्टि करने वाले टेस्ट उपलब्ध नहीं हैं. readelf
का इस्तेमाल करके और सेगमेंट फ़्लैग की जांच करके, बाइनरी की मैन्युअल तौर पर पुष्टि की जा सकती है.