নিয়ন্ত্রণ প্রবাহ অখণ্ডতা

২০১৬ সাল পর্যন্ত, অ্যান্ড্রয়েডের প্রায় ৮৬% দুর্বলতাই মেমরি সুরক্ষা সম্পর্কিত। বেশিরভাগ দুর্বলতার সুযোগ নিয়ে আক্রমণকারীরা একটি অ্যাপের স্বাভাবিক নিয়ন্ত্রণ প্রবাহ পরিবর্তন করে, এবং সেই ব্যবহৃত অ্যাপটির সমস্ত সুবিধা নিয়ে যথেচ্ছ ক্ষতিকারক কার্যকলাপ সম্পাদন করে। কন্ট্রোল ফ্লো ইন্টিগ্রিটি (CFI) একটি সুরক্ষা ব্যবস্থা যা একটি কম্পাইল করা বাইনারির মূল নিয়ন্ত্রণ প্রবাহ গ্রাফে পরিবর্তন করতে বাধা দেয়, ফলে এই ধরনের আক্রমণ চালানো উল্লেখযোগ্যভাবে কঠিন হয়ে পড়ে।

অ্যান্ড্রয়েড ৮.১-এ আমরা মিডিয়া স্ট্যাকে LLVM-এর CFI বাস্তবায়ন সক্রিয় করেছিলাম। অ্যান্ড্রয়েড ৯-এ আমরা আরও বেশি কম্পোনেন্ট এবং কার্নেলেও CFI সক্রিয় করেছি। সিস্টেম CFI ডিফল্টরূপে চালু থাকে, কিন্তু আপনাকে কার্নেল CFI সক্রিয় করতে হবে।

LLVM-এর CFI-এর জন্য লিঙ্ক-টাইম অপটিমাইজেশন (LTO) সহ কম্পাইল করা প্রয়োজন। LTO লিঙ্ক-টাইম পর্যন্ত অবজেক্ট ফাইলের LLVM বিটকোড উপস্থাপনা সংরক্ষণ করে, যা কম্পাইলারকে কী কী অপটিমাইজেশন করা যেতে পারে সে সম্পর্কে আরও ভালোভাবে সিদ্ধান্ত নিতে সাহায্য করে। LTO সক্রিয় করলে চূড়ান্ত বাইনারির আকার কমে এবং পারফরম্যান্স উন্নত হয়, কিন্তু কম্পাইল টাইম বেড়ে যায়। অ্যান্ড্রয়েডে পরীক্ষা করে দেখা গেছে যে, LTO এবং CFI-এর সংমিশ্রণের ফলে কোডের আকার এবং পারফরম্যান্সে নগণ্য ওভারহেড তৈরি হয়; কয়েকটি ক্ষেত্রে উভয়ই উন্নত হয়েছে।

CFI এবং অন্যান্য ফরওয়ার্ড-কন্ট্রোল চেকগুলো কীভাবে পরিচালনা করা হয় সে সম্পর্কে আরও প্রযুক্তিগত বিবরণের জন্য, LLVM ডিজাইন ডকুমেন্টেশন দেখুন।

উদাহরণ এবং উৎস

CFI কম্পাইলার দ্বারা সরবরাহ করা হয় এবং এটি কম্পাইল করার সময় বাইনারিতে ইন্সট্রুমেন্টেশন যোগ করে। আমরা AOSP-তে Clang টুলচেইন এবং অ্যান্ড্রয়েড বিল্ড সিস্টেমে CFI সমর্থন করি।

/platform/build/target/product/cfi-common.mk এ থাকা কম্পোনেন্টগুলোর সেটের জন্য Arm64 ডিভাইসগুলোতে CFI ডিফল্টরূপে সক্রিয় থাকে। এটি একগুচ্ছ মিডিয়া কম্পোনেন্টের মেকফাইল/ব্লুপ্রিন্ট ফাইলেও সরাসরি সক্রিয় করা থাকে, যেমন /platform/frameworks/av/media/libmedia/Android.bp এবং /platform/frameworks/av/cmds/stagefright/Android.mk

সিস্টেম CFI বাস্তবায়ন

আপনি যদি Clang এবং Android বিল্ড সিস্টেম ব্যবহার করেন, তাহলে CFI ডিফল্টরূপে সক্রিয় থাকে। যেহেতু CFI Android ব্যবহারকারীদের সুরক্ষিত রাখতে সাহায্য করে, তাই আপনার এটি নিষ্ক্রিয় করা উচিত নয়।

প্রকৃতপক্ষে, আমরা আপনাকে অতিরিক্ত কম্পোনেন্টগুলোর জন্য CFI সক্রিয় করতে জোরালোভাবে উৎসাহিত করি। এর জন্য আদর্শ হলো প্রিভিলেজড নেটিভ কোড, অথবা এমন নেটিভ কোড যা অবিশ্বস্ত ব্যবহারকারীর ইনপুট প্রসেস করে। আপনি যদি ক্ল্যাং (clang) এবং অ্যান্ড্রয়েড বিল্ড সিস্টেম ব্যবহার করেন, তাহলে আপনার মেকফাইল বা ব্লুপ্রিন্ট ফাইলে কয়েকটি লাইন যোগ করে নতুন কম্পোনেন্টগুলোতে CFI সক্রিয় করতে পারেন।

মেকফাইলে CFI সমর্থন করা

/platform/frameworks/av/cmds/stagefright/Android.mk এর মতো একটি মেক ফাইলে CFI সক্রিয় করতে, যোগ করুন:

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
  • বিল্ড চলাকালীন LOCAL_SANITIZE স্যানিটাইজার হিসেবে CFI-কে নির্দিষ্ট করে।
  • LOCAL_SANITIZE_DIAG CFI-এর জন্য ডায়াগনস্টিক মোড চালু করে। ক্র্যাশের সময় ডায়াগনস্টিক মোড লগক্যাটে অতিরিক্ত ডিবাগ তথ্য প্রিন্ট করে, যা আপনার বিল্ড তৈরি এবং পরীক্ষা করার সময় সহায়ক। তবে, প্রোডাকশন বিল্ডে ডায়াগনস্টিক মোড বন্ধ করে দিতে ভুলবেন না।
  • LOCAL_SANITIZE_BLACKLIST কম্পোনেন্টগুলোকে নির্দিষ্ট ফাংশন বা সোর্স ফাইলের জন্য CFI ইন্সট্রুমেন্টেশন বেছে বেছে নিষ্ক্রিয় করার সুযোগ দেয়। অন্যথায় উদ্ভূত হতে পারে এমন ব্যবহারকারী-মুখী সমস্যা সমাধানের জন্য আপনি শেষ উপায় হিসেবে একটি ব্ল্যাকলিস্ট ব্যবহার করতে পারেন। আরও বিস্তারিত জানতে, Disabling CFI দেখুন।

ব্লুপ্রিন্ট ফাইলে CFI সমর্থন করা

/platform/frameworks/av/media/libmedia/Android.bp এর মতো একটি ব্লুপ্রিন্ট ফাইলে CFI সক্রিয় করতে, যোগ করুন:

   sanitize: {
        cfi: true,
        diag: {
            cfi: true,
        },
        blacklist: "cfi_blacklist.txt",
    },

সমস্যা সমাধান

আপনি যদি নতুন কম্পোনেন্টগুলিতে CFI সক্রিয় করেন, তাহলে আপনি ফাংশন টাইপ অমিল ত্রুটি এবং অ্যাসেম্বলি কোড টাইপ অমিল ত্রুটির মতো কিছু সমস্যার সম্মুখীন হতে পারেন।

ফাংশন টাইপ অমিলের ত্রুটি ঘটে কারণ CFI পরোক্ষ কলগুলোকে শুধুমাত্র সেইসব ফাংশনে জাম্প করার জন্য সীমাবদ্ধ করে, যেগুলোর ডাইনামিক টাইপ কলে ব্যবহৃত স্ট্যাটিক টাইপের সমান। CFI ভার্চুয়াল এবং নন-ভার্চুয়াল মেম্বার ফাংশন কলগুলোকে শুধুমাত্র সেইসব অবজেক্টে জাম্প করার জন্য সীমাবদ্ধ করে, যেগুলো কল করতে ব্যবহৃত অবজেক্টের স্ট্যাটিক টাইপের একটি ডিরাইভড ক্লাস। এর মানে হলো, যখন আপনার কোড এই অনুমানগুলোর কোনো একটি লঙ্ঘন করে, তখন CFI দ্বারা যুক্ত করা ইন্সট্রুমেন্টেশনটি অ্যাবোর্ট হয়ে যাবে। উদাহরণস্বরূপ, স্ট্যাক ট্রেসে একটি SIGABRT দেখা যায় এবং লগক্যাটে কন্ট্রোল ফ্লো ইন্টিগ্রিটি একটি অমিল খুঁজে পেয়েছে এমন একটি লাইন থাকে।

এটি ঠিক করতে, নিশ্চিত করুন যে কল করা ফাংশনটির টাইপ স্ট্যাটিক্যালি ডিক্লেয়ার করা টাইপের মতোই। এখানে দুটি উদাহরণ CL দেওয়া হলো:

আরেকটি সম্ভাব্য সমস্যা হলো এমন কোডে CFI সক্রিয় করার চেষ্টা করা, যেখানে অ্যাসেম্বলি কোডের পরোক্ষ কল রয়েছে। যেহেতু অ্যাসেম্বলি কোড টাইপযুক্ত নয়, এর ফলে টাইপ মিসম্যাচ ঘটে।

এটি সমাধান করতে, প্রতিটি অ্যাসেম্বলি কলের জন্য নেটিভ কোড র‍্যাপার তৈরি করুন এবং র‍্যাপারগুলোকে কলিং পয়েন্টারের মতো একই ফাংশন সিগনেচার দিন। এরপর র‍্যাপারটি সরাসরি অ্যাসেম্বলি কোড কল করতে পারবে। যেহেতু ডিরেক্ট ব্রাঞ্চগুলো CFI দ্বারা ইন্সট্রুমেন্টেড হয় না (রানটাইমে এগুলোকে রিপয়েন্ট করা যায় না এবং তাই কোনো নিরাপত্তা ঝুঁকি তৈরি করে না), এটি সমস্যার সমাধান করবে।

যদি অ্যাসেম্বলি ফাংশনের সংখ্যা অনেক বেশি হয় এবং সেগুলোর সবগুলোকে ঠিক করা সম্ভব না হয়, তাহলে আপনি অ্যাসেম্বলিতে পরোক্ষ কল রয়েছে এমন সমস্ত ফাংশনকে ব্ল্যাকলিস্ট করতে পারেন। এটি করার পরামর্শ দেওয়া হয় না, কারণ এটি এই ফাংশনগুলিতে CFI চেক নিষ্ক্রিয় করে দেয়, ফলে আক্রমণের সুযোগ তৈরি হয়।

সিএফআই অক্ষম করা

আমরা কোনো পারফরম্যান্স ওভারহেড লক্ষ্য করিনি, তাই আপনার CFI নিষ্ক্রিয় করার প্রয়োজন হবে না। তবে, যদি ব্যবহারকারীর উপর কোনো প্রভাব পড়ে, তাহলে আপনি কম্পাইল করার সময় একটি স্যানিটাইজার ব্ল্যাকলিস্ট ফাইল সরবরাহ করে স্বতন্ত্র ফাংশন বা সোর্স ফাইলের জন্য বেছে বেছে CFI নিষ্ক্রিয় করতে পারেন। এই ব্ল্যাকলিস্টটি কম্পাইলারকে নির্দিষ্ট স্থানে CFI ইন্সট্রুমেন্টেশন নিষ্ক্রিয় করার নির্দেশ দেয়।

অ্যান্ড্রয়েড বিল্ড সিস্টেম Make এবং Soong উভয়ের জন্যই প্রতি-কম্পোনেন্ট ব্ল্যাকলিস্ট সমর্থন করে (যা আপনাকে এমন সোর্স ফাইল বা স্বতন্ত্র ফাংশন বেছে নিতে দেয় যেগুলোতে CFI ইন্সট্রুমেন্টেশন করা হবে না)। একটি ব্ল্যাকলিস্ট ফাইলের ফরম্যাট সম্পর্কে আরও বিস্তারিত জানতে, আপস্ট্রিম Clang ডক্স দেখুন।

বৈধতা

বর্তমানে, CFI-এর জন্য বিশেষভাবে কোনো CTS টেস্ট নেই। এর পরিবর্তে, CFI চালু থাকা বা না থাকা উভয় অবস্থাতেই CTS টেস্টগুলো পাস করছে কিনা তা নিশ্চিত করুন, যাতে যাচাই করা যায় যে CFI ডিভাইসটিকে প্রভাবিত করছে না।