إضافة وضع علامات الذاكرة (MTE)

لماذا تستخدم إضافة MTE؟

أخطاء متعلقة بأمان الذاكرة، وهي أخطاء في معالجة الذاكرة في البرمجة الأصلية واللغات، وهي مشاكل شائعة في التعليمات البرمجية. تؤدي إلى ثغرات أمنية بالإضافة إلى مشكلات الاستقرار.

طرح Armv9 إضافة وضع علامات الذاكرة (MTE)، وهي عبارة عن جهاز التي تتيح لك اكتشاف أخطاء الاستخدام بعد تفريغها والتجاوز في المخزن المؤقت التعليمات البرمجية الأصلية.

التحقق من الدعم

بدءًا من نظام التشغيل Android 13، ستتيح أجهزة محدّدة تفعيل إضافة وضع علامات الذاكرة (MTE). للتأكّد مما إذا كان جهازك يعمل مع تفعيل إضافة وضع علامات الذاكرة (MTE)، يمكنك تشغيل ما يلي: :

adb shell grep mte /proc/cpuinfo

إذا كانت النتيجة Features : [...] mte، يعني ذلك أنّ جهازك يعمل بميزة MTE. مفعّلة.

لا تفعِّل بعض الأجهزة هذه الإضافة تلقائيًا، ولكنّها تسمح للمطوّرين بإعادة التشغيل باستخدام تم تفعيل إضافة وضع علامات الذاكرة (MTE). هذه تهيئة تجريبية لا يوصى بها مع الاستخدام العادي، حيث قد يؤدي ذلك إلى انخفاض أداء الجهاز أو ثباته، ولكن قد يتسبّب في مفيدة لتطوير التطبيق. للوصول إلى هذا الوضع، انتقل إلى خيارات المطوّرين > إضافة وضع علامات الذاكرة في تطبيق "الإعدادات" إذا كان هذا غير متوفر، لا يتيح جهازك تفعيل MTE بهذه الطريقة.

أوضاع التشغيل الخاصة بـ MTE

تدعم إضافة MTE وضعين: SYNC وASYNC. يوفِّر وضع SYNC تشخيصًا أفضل. معلومات أكثر ملاءمةً لأغراض التطوير، فوضع ASYNC أداءً عاليًا يسمح بتفعيله للتطبيقات التي تم إصدارها.

الوضع المتزامن (المزامنة)

تم تحسين هذا الوضع لإتاحة تصحيح الأخطاء مقارنةً بالأداء، ويمكن أن استخدامها كأداة دقيقة للكشف عن الأخطاء، في حال ارتفاع أعباء الأداء مقبول. وعند تفعيل هذه الميزة، تعمل أيضًا ميزة MTE SYNC أيضًا كإجراء للحدّ من الأمان.

في حالة عدم تطابق العلامة، ينهي المعالج العملية على التحميل المسيء أو تخزين التعليمات باستخدام SIGSEGV (مع si_code SEGV_MTESERR) والمعلومات الكاملة حول الوصول إلى الذاكرة والعنوان الذي يتضمن عيوبًا.

يفيد هذا الوضع أثناء الاختبار كبديل أسرع لنظام HWASan الذي لا يتطلب منك إعادة تحويل الرمز البرمجي أو في مرحلة الإصدار العلني عندما يتضمّن تطبيقك تمثل الأجزاء المعرضة للهجوم. بالإضافة إلى ذلك، عندما يجد وضع ASYNC (الموضح أدناه) خطأ، يمكن الحصول على تقرير خطأ دقيق باستخدام واجهات برمجة التطبيقات لوقت التشغيل للتبديل التنفيذ إلى وضع المزامنة.

علاوة على ذلك، عند التشغيل في وضع المزامنة، يسجل تخصيص Android تتبع تسلسل استدعاء الدوال البرمجية لكل عملية تخصيص وصفقة وتستخدمها لتقديم نتائج تقارير الأخطاء التي تتضمن شرحًا لخطأ في الذاكرة، مثل استخدام "بعد الاستخدام" أو تجاوز سعة المخزن المؤقت، وتتبع تسلسل استدعاء الدوال البرمجية للذاكرة ذات الصلة الأحداث (يمكنك الاطّلاع على فهم تقارير MTE للحصول على مزيد من التفاصيل). مثل وتوفر المزيد من المعلومات السياقية وتسهل تتبع الأخطاء أكثر مما كان عليه في وضع ASYNC.

الوضع غير المتزامن (ASYNC)

تم تحسين هذا الوضع ليتناسب مع أدائه مقارنةً بدقة تقارير الأخطاء، ويمكن استخدامه تُستخدم لرصد أخطاء أمان الذاكرة من أي وقت مضى. في حال عدم تطابق العلامة، يستمر المعالج في التنفيذ حتى إدخال أقرب kernel (مثل بروتوكول syscall أو إيقاف المؤقت)، حيث يتم إنهاء العملية باستخدام SIGSEGV (الرمز SEGV_MTEAERR) بدون تسجيل العنوان الخطأ أو الوصول إلى الذاكرة.

هذا الوضع مفيد للتخفيف من ثغرات أمان الذاكرة في على قواعد رموز تم اختبارها جيدًا حيث يتم رصد كثافة أخطاء أمان الذاكرة والذي يعرف بانخفاضه، ويتحقق ذلك باستخدام وضع "المزامنة" أثناء الاختبار.

تفعيل إضافة وضع علامات الذاكرة (MTE)

لجهاز واحد

لإجراء التجارب، يمكن استخدام تغييرات توافق التطبيقات لضبط القيمة التلقائية قيمة السمة memtagMode لتطبيق لا يحدّد أي قيمة في البيان (أو يحدد "default").

يمكن العثور على هذه الأنظمة ضمن النظام > متقدم > خيارات المطوّرين > حملة تطبيقات تغييرات التوافق في قائمة الإعدادات العامة. إعداد NATIVE_MEMTAG_ASYNC أو NATIVE_MEMTAG_SYNC تفعِّل MTE لتطبيق معيّن.

وبدلاً من ذلك، يمكن ضبط ذلك باستخدام الأمر am على النحو التالي:

  • بالنسبة إلى وضع المزامنة: $ adb shell am compat enable NATIVE_MEMTAG_SYNC my.app.name
  • بالنسبة إلى وضع ASYNC: $ adb shell am compat enable NATIVE_MEMTAG_ASYNC my.app.name

داخل Gradle

ويمكنك تفعيل إضافة وضع علامات الذاكرة (MTE) لجميع إصدارات تصحيح الأخطاء لمشروع Gradle من خلال وضع

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application android:memtagMode="sync" tools:replace="android:memtagMode"/>
</manifest>

إلى app/src/debug/AndroidManifest.xml. سيؤدي هذا الإجراء إلى إلغاء إعدادات memtagMode مع مزامنة إصدارات تصحيح الأخطاء.

ويمكنك بدلاً من ذلك تفعيل إضافة وضع علامات الذاكرة (MTE) لجميع إصدارات نوع إنشاء مخصّص. للقيام بذلك، إذًا، عليك إنشاء نوع الإنشاء الخاص بك ووضع علامة XML إلى app/src/<name of buildType>/AndroidManifest.xml.

لملف APK على أي جهاز متوافق

تكون إضافة وضع علامات الذاكرة (MTE) غير مفعَّلة تلقائيًا. بإمكان التطبيقات التي تريد استخدام ميزة MTE أن يمكنك إجراء ذلك من خلال ضبط android:memtagMode ضمن <application> أو <process>. في AndroidManifest.xml.

android:memtagMode=(off|default|sync|async)

عند ضبط السمة على علامة <application>، تؤثر السمة في جميع العمليات المستخدَمة. بواسطة التطبيق، ويمكن تجاوزها في العمليات الفردية من خلال وضع العلامة <process>.

الإنشاء باستخدام أدوات قياس الأداء

يساعد تفعيل إضافة وضع علامات الذاكرة (MTE) كما هو موضَّح سابقًا في رصد أخطاء تلف الذاكرة على لقطة لأجزاء من الذاكرة الأصلية. للكشف عن تلف الذاكرة على المكدس، بالإضافة إلى تمكين إضافة وضع علامات الذاكرة (MTE) للتطبيق، ويجب إعادة إنشاء الرمز باستخدام قياس حالة التطبيق. تشير رسالة الأشكال البيانية لن يتم تشغيل التطبيق الناتج إلا على الأجهزة المتوافقة مع MTE.

لإنشاء الرمز الأصلي (JNI) لتطبيقك باستخدام ميزة MTE، عليك اتّباع الخطوات التالية:

لعبة ndk-build

في ملف Application.mk:

APP_CFLAGS := -fsanitize=memtag -fno-omit-frame-pointer -march=armv8-a+memtag
APP_LDFLAGS := -fsanitize=memtag -fsanitize-memtag-mode=sync -march=armv8-a+memtag

إنشاء فيديوهات Shorts

لكل هدف في CMakeLists.txt:

target_compile_options(${TARGET} PUBLIC -fsanitize=memtag -fno-omit-frame-pointer -march=armv8-a+memtag)
target_link_options(${TARGET} PUBLIC -fsanitize=memtag -fsanitize-memtag-mode=sync -march=armv8-a+memtag)

تشغيل تطبيقك

بعد تفعيل إضافة وضع علامات الذاكرة (MTE)، يمكنك استخدام تطبيقك واختباره كالمعتاد. إذا كانت هناك مشكلة في أمان الذاكرة يتعطّل على تطبيقك مع ظهور علامة ضريح يشبه ذلك (ملاحظة SIGSEGV المزود بـ SEGV_MTESERR للمزامنة أو SEGV_MTEAERR for ASYNC):

pid: 13935, tid: 13935, name: sanitizer-statu  >>> sanitizer-status <<<
uid: 0
tagged_addr_ctrl: 000000000007fff3
signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x800007ae92853a0
Cause: [MTE]: Use After Free, 0 bytes into a 32-byte allocation at 0x7ae92853a0
x0  0000007cd94227cc  x1  0000007cd94227cc  x2  ffffffffffffffd0  x3  0000007fe81919c0
x4  0000007fe8191a10  x5  0000000000000004  x6  0000005400000051  x7  0000008700000021
x8  0800007ae92853a0  x9  0000000000000000  x10 0000007ae9285000  x11 0000000000000030
x12 000000000000000d  x13 0000007cd941c858  x14 0000000000000054  x15 0000000000000000
x16 0000007cd940c0c8  x17 0000007cd93a1030  x18 0000007cdcac6000  x19 0000007fe8191c78
x20 0000005800eee5c4  x21 0000007fe8191c90  x22 0000000000000002  x23 0000000000000000
x24 0000000000000000  x25 0000000000000000  x26 0000000000000000  x27 0000000000000000
x28 0000000000000000  x29 0000007fe8191b70
lr  0000005800eee0bc  sp  0000007fe8191b60  pc  0000005800eee0c0  pst 0000000060001000

backtrace:
      #00 pc 00000000000010c0  /system/bin/sanitizer-status (test_crash_malloc_uaf()+40) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #01 pc 00000000000014a4  /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #02 pc 00000000000019cc  /system/bin/sanitizer-status (main+1032) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #03 pc 00000000000487d8  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+96) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)

deallocated by thread 13935:
      #00 pc 000000000004643c  /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::quarantineOrDeallocateChunk(scudo::Options, void*, scudo::Chunk::UnpackedHeader*, unsigned long)+688) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #01 pc 00000000000421e4  /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::deallocate(void*, scudo::Chunk::Origin, unsigned long, unsigned long)+212) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #02 pc 00000000000010b8  /system/bin/sanitizer-status (test_crash_malloc_uaf()+32) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #03 pc 00000000000014a4  /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)

allocated by thread 13935:
      #00 pc 0000000000042020  /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::allocate(unsigned long, scudo::Chunk::Origin, unsigned long, bool)+1300) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #01 pc 0000000000042394  /apex/com.android.runtime/lib64/bionic/libc.so (scudo_malloc+36) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #02 pc 000000000003cc9c  /apex/com.android.runtime/lib64/bionic/libc.so (malloc+36) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
      #03 pc 00000000000010ac  /system/bin/sanitizer-status (test_crash_malloc_uaf()+20) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
      #04 pc 00000000000014a4  /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
Learn more about MTE reports: https://source.android.com/docs/security/test/memory-safety/mte-report

راجِع فهم تقارير MTE في مستندات AOSP للحصول على مزيد من التفاصيل. إِنْتَ أيضًا تصحيح أخطاء التطبيق باستخدام "استوديو Android" ويتوقف برنامج تصحيح الأخطاء في السطر الذي يتسبب في وصول غير صالح إلى الذاكرة.

المستخدمون المتقدمون: استخدام ميزة MTE في التخصيص الخاص بك

لاستخدام ميزة MTE للذاكرة التي لا يتم تخصيصها من خلال مخصصات النظام العادي، يجب إلى تعديل التخصيص لوضع علامة على الذاكرة والمؤشرات.

يجب تخصيص صفحات تخصيص الصفحات باستخدام PROT_MTE في علامة prot للسمة mmap (أو mprotect)

يجب أن تتوافق جميع التخصيصات التي تم وضع علامات عليها مع 16 بايت، لأنّه يمكن تعيين العلامات فقط. للمقاطع 16 بايت (المعروفة أيضًا بالحبيبات).

بعد ذلك، قبل عرض مؤشر الماوس، عليك استخدام تعليمات IRG لتنفيذ ما يلي: إنشاء علامة عشوائية وتخزينها في المؤشر.

اتّبِع التعليمات التالية لوضع علامة على الذاكرة الأساسية:

  • STG: وضع علامة على حبيبة واحدة بحجم 16 بايت
  • ST2G: ضَع علامة على حبيتَين بحجم 16 بايت.
  • DC GVA: سطر مؤقت للعلامة يتضمّن العلامة نفسها

بدلاً من ذلك، تؤدي التعليمات التالية أيضًا إلى تهيئة الذاكرة تمامًا:

  • STZG: وضع علامة على حبيبة واحدة بحجم 16 بايت وعدم إعدادها
  • STZ2G: وضع علامة على حبيتَين بحجم 16 بايت وإعدادهما صفر
  • DC GZVA: إضافة علامة وتهيئة ملف التخزين المؤقت بدون إعداد باستخدام العلامة نفسها

تجدر الإشارة إلى أنّ هذه التعليمات غير متاحة على وحدات المعالجة المركزية القديمة، لذا يجب قم بتشغيلها بشكل مشروط عند تفعيل MTE. يمكنك التحقق مما إذا كانت إضافة وضع علامات الذاكرة (MTE) قيد المعالجة للعملية:

#include <sys/prctl.h>

bool runningWithMte() {
      int mode = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
      return mode != -1 && mode & PR_MTE_TCF_MASK;
}

يمكنك الاستفادة من تنفيذ scudo كمرجع.

مزيد من المعلومات

يمكنك معرفة المزيد من المعلومات من خلال دليل مستخدم MTE لنظام التشغيل Android الذي كتبه Arm.