UndefinedBehaviorSanitizer

تنفّذ أداة UndefinedBehaviorSanitizer (UBSan) عملية تتبُّع في وقت الترجمة للتحقّق من أنواع مختلفة من السلوك غير المحدّد. على الرغم من أنّ UBSan قادرة على رصد العديد من الأخطاء المتعلقة بالسلوك غير المحدّد، يتيح نظام التشغيل Android ما يلي:

  • المحاذاة
  • bool
  • bounds
  • enum
  • float-cast-overflow
  • float-divide-by-zero
  • integer-divide-by-zero
  • nonnull-attribute
  • قيمة فارغة
  • العودة
  • returns-nonnull-attribute
  • shift-base
  • shift-exponent
  • signed-integer-overflow
  • لا يمكن الوصول
  • unsigned-integer-overflow
  • vla-bound

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

التنفيذ

في نظام إنشاء Android، يمكنك تفعيل UBSan على مستوى العالم أو على المستوى المحلي. لتفعيل UBSan على مستوى العالم، اضبط SANITIZE_TARGET في Android.mk. لتفعيل UBSan على مستوى كل وحدة، اضبط LOCAL_SANITIZE وحدِّد السلوكيات غير المحدّدة التي تريد البحث عنها في Android.mk. مثلاً:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

وإعدادات مخطط البناء المكافئ (Android.bp):

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            misc_undefined: [
                "alignment",
                "bounds",
                "null",
                "unreachable",
                "integer",
            ],
        },
    },

}

اختصارات UBSan

يتضمّن Android أيضًا اختصارَين، integer وdefault-ub، لتفعيل مجموعة من أدوات التنظيف في الوقت نفسه. يفعّل الاختصار integer أدوات integer-divide-by-zero وsigned-integer-overflow وunsigned-integer-overflow. تتيح default-ub عمليات التحقّق التي تتسبّب بأقل قدر من مشاكل أداء برنامج الترجمة: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound. يمكن استخدام فئة تنظيف الأعداد الصحيحة مع SANITIZE_TARGET وLOCAL_SANITIZE، بينما يمكن استخدام default-ub مع SANITIZE_TARGET فقط.

الإبلاغ عن الأخطاء بشكل أفضل

يستدعي التنفيذ التلقائي لـ UBSan في Android دالة محدّدة عند مواجهة سلوك غير محدّد. تكون هذه الدالة في الوضع abort تلقائيًا. ومع ذلك، بدءًا من تشرين الأول (أكتوبر) 2016، أصبح بإمكان UBSan على Android استخدام مكتبة وقت التشغيل الاختيارية التي توفّر تقارير أكثر تفصيلاً عن الأخطاء، بما في ذلك نوع السلوك غير المحدّد الذي تم رصده، ومعلومات عن الملف وسطر رمز المصدر. لتفعيل ميزة إعداد التقارير عن هذا الخطأ باستخدام عمليات التحقّق من الأعداد الصحيحة، أضِف ما يلي إلى ملف Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

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

في ما يلي مثال على المعلومات التي توفّرها مكتبة وقت التشغيل UBSan:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

تنقيح تجاوز سعة الأعداد الصحيحة

يمكن أن تتسبّب حالات تجاوز سعة الأعداد الصحيحة غير المقصودة في حدوث ثغرات أمنية تؤدي إلى تلف الذاكرة أو الكشف عن المعلومات في المتغيرات المرتبطة بعمليات الوصول إلى الذاكرة أو عمليات تخصيص الذاكرة. ولمكافحة ذلك، أضفنا أدوات تنظيف الأعداد الصحيحة الموقّعة وغير الموقّعة التي تتجاوز الحد الأقصى إلى UndefinedBehaviorSanitizer (UBSan) في Clang، وذلك لتعزيز إطار عمل الوسائط في Android 7.0. في نظام التشغيل Android 9، وسّعنا نطاق تغطية UBSan ليشمل المزيد من المكوّنات وحسّنّا توافق نظام الإصدار معها.

تم تصميم هذه الميزة لإضافة عمليات تحقّق حول العمليات الحسابية / التعليمات التي قد يحدث فيها تجاوز للحد الأقصى، وذلك لإيقاف العملية بأمان في حال حدوث تجاوز. يمكن أن تقلّل أدوات التنظيف هذه من فئة كاملة من الثغرات الأمنية المتعلقة بتلف الذاكرة والإفصاح عن المعلومات، حيث يكون السبب الأساسي هو تجاوز سعة عدد صحيح، مثل ثغرة Stagefright الأمنية الأصلية.

أمثلة ومصدر

توفّر أداة Integer Overflow Sanitization (IntSan) من خلال برنامج التجميع، وتضيف هذه الأداة تعليمات برمجية إلى الملف الثنائي أثناء وقت التجميع لرصد حالات تجاوز سعة الأعداد الصحيحة. يتم تفعيلها تلقائيًا في مكوّنات مختلفة في جميع أنحاء النظام الأساسي، مثل /platform/external/libnl/Android.bp.

التنفيذ

تستخدم أداة IntSan أدوات تنظيف الأعداد الصحيحة الموقّعة وغير الموقّعة في UBSan. يتم تفعيل إجراء التخفيف هذا على مستوى كل وحدة. ويساعد في الحفاظ على أمان المكوّنات المهمة في Android، لذا لا يجب إيقافه.

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

إتاحة IntSan في ملفات makefile

لتفعيل IntSan في ملف makefile، أضِف ما يلي:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • تأخذ LOCAL_SANITIZE قائمة أدوات تنظيف مفصولة بفواصل، مع كون integer_overflow مجموعة خيارات مُعدّة مسبقًا لأدوات تنظيف تجاوز سعة الأعداد الصحيحة الموقّعة وغير الموقّعة، مع قائمة حظر تلقائية.
  • يؤدي LOCAL_SANITIZE_DIAG إلى تفعيل وضع التشخيص لأدوات التنظيف. يجب استخدام وضع التشخيص أثناء الاختبار فقط لأنّه لن يتم إيقاف العملية عند حدوث تجاوزات، ما يؤدي إلى إبطال ميزة الأمان التي توفّرها إجراءات التخفيف. يمكنك الاطّلاع على تحديد المشاكل وحلّها لمزيد من التفاصيل.
  • تتيح لك السمة LOCAL_SANITIZE_BLOCKLIST تحديد ملف BLOCKLIST لمنع تنظيف الدوال وملفات المصدر. يمكنك الاطّلاع على مقالة تحديد المشاكل وحلّها للحصول على مزيد من التفاصيل.

إذا أردت تحكّمًا أكثر دقة، فعِّل أدوات التنظيف بشكل فردي باستخدام أحد الخيارَين التاليَين أو كليهما:

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
    LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

إتاحة IntSan في ملفات المخططات التفصيلية

لتفعيل تنظيف تجاوز عدد صحيح في ملف مخطط، مثل /platform/external/libnl/Android.bp، أضِف ما يلي:

   sanitize: {
          integer_overflow: true,
          diag: {
              integer_overflow: true,
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

كما هو الحال مع ملفات الإنشاء، فإنّ السمة integer_overflow هي مجموعة مسبقة التجميع من الخيارات الخاصة بأدوات التنظيف الفردية التي تتعامل مع تجاوز سعة الأعداد الصحيحة الموقّعة وغير الموقّعة، مع قائمة حظر تلقائية.

تتيح مجموعة السمات diag تفعيل وضع التشخيص لأدوات التنظيف. استخدِم وضع بيانات التشخيص أثناء الاختبار فقط. لا يتوقف وضع التصحيح عند حدوث تجاوزات، ما يؤدي إلى إبطال ميزة الأمان التي توفّرها إجراءات التخفيف في الإصدارات المخصّصة للمستخدمين. يمكنك الاطّلاع على تحديد المشاكل وحلّها لمعرفة المزيد من التفاصيل.

تسمح السمة BLOCKLIST بتحديد ملف BLOCKLIST يسمح للمطوّرين بمنع تنظيف الدوال وملفات المصدر. يمكنك الاطّلاع على تحديد المشاكل وحلّها لمعرفة المزيد من التفاصيل.

لتفعيل أدوات التنظيف بشكل فردي، استخدِم:

   sanitize: {
          misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
          diag: {
              misc_undefined: ["signed-integer-overflow",
                               "unsigned-integer-overflow",],
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

تحديد المشاكل وحلّها

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

للعثور على عمليات إيقاف ناتجة عن التنظيف في إصدارات المستخدمين، ابحث عن SIGABRT الأعطال التي تتضمّن رسائل إيقاف تشير إلى رصد تجاوز سعة من خلال UBSan، مثل:

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: sub-overflow'

يجب أن يتضمّن تتبُّع تسلسل استدعاء الدوال الدالة التي تتسبّب في الإيقاف، ولكن قد لا تظهر حالات تجاوز سعة المخزن المؤقت التي تحدث في الدوال المضمّنة في تتبُّع تسلسل استدعاء الدوال.

لتحديد السبب الأساسي بسهولة أكبر، فعِّل بيانات التشخيص في المكتبة التي تؤدي إلى إيقاف العملية وحاوِل إعادة إظهار الخطأ. عند تفعيل بيانات التشخيص، لن يتم إيقاف العملية، بل ستستمر في التنفيذ. يساعد عدم إيقاف التنفيذ في تحقيق الحد الأقصى من عدد حالات تجاوز سعة المخزن المؤقت غير الضارة في مسار تنفيذ معيّن بدون الحاجة إلى إعادة الترجمة بعد إصلاح كل خطأ. تنتج &quot;بيانات التشخيص&quot; رسالة خطأ تتضمّن رقم السطر وملف المصدر الذي تسبّب في الإيقاف:

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

بعد تحديد العملية الحسابية التي تتضمّن مشكلة، تأكَّد من أنّ تجاوز سعة التخزين غير ضار ومقصود (على سبيل المثال، ليس له أي آثار أمنية). يمكنك معالجة عملية الإيقاف المفاجئ لأداة التنظيف من خلال:

  • إعادة تصميم الرمز البرمجي لتجنُّب التجاوز (مثال)
  • التحقّق من تجاوز سعة التخزين بشكل صريح باستخدام دوال __builtin_*_overflow في Clang (مثال)
  • إيقاف عملية التنظيف في الدالة من خلال تحديد السمة no_sanitize (مثال)
  • إيقاف تنظيف دالة أو ملف مصدر من خلال ملف BLOCKLIST (مثال)

يجب استخدام الحلّ الأكثر تفصيلاً الممكن. على سبيل المثال، يجب إعادة تصميم دالة كبيرة تتضمّن العديد من العمليات الحسابية وعملية واحدة تتجاوز الحد المسموح به، بدلاً من إدراج الدالة بأكملها في القائمة المحظورة.

تشمل الأنماط الشائعة التي قد تؤدي إلى تجاوزات غير ضارة ما يلي:

  • عمليات التحويل الضمني التي يحدث فيها تجاوز الحد الأقصى غير الموقّع قبل التحويل إلى نوع موقّع (مثال)
  • عمليات حذف من قائمة مرتبطة تؤدي إلى إنقاص فهرس الحلقة عند الحذف (مثال)
  • تعيين نوع غير موقّع إلى -1 بدلاً من تحديد الحد الأقصى الفعلي للقيمة (مثال)
  • حلقات التكرار التي تقلّل عددًا صحيحًا غير موقّع في الشرط (مثال، مثال)

ننصح المطوّرين بالتأكّد من أنّ الحالات التي يرصد فيها برنامج التنظيف تجاوزًا للحدود هي حالات غير ضارة ولا تتضمّن أي آثار جانبية غير مقصودة أو أي آثار أمنية قبل إيقاف التنظيف.

إيقاف IntSan

يمكنك إيقاف IntSan باستخدام BLOCKLISTs أو سمات الدوال. يجب إيقافها بشكل محدود وعندما يكون إعادة تصميم الرمز البرمجي غير منطقية أو إذا كان هناك عبء أداء إشكالي.

راجِع مستندات Clang المصدرية للحصول على مزيد من المعلومات حول إيقاف IntSan باستخدام سمات الدوال وتنسيق ملف القائمة المحظورة. يجب أن يكون نطاق القائمة المحظورة محددًا لأداة التعقيم المعيّنة من خلال استخدام أسماء أقسام تحدد أداة التعقيم المستهدَفة لتجنُّب التأثير في أدوات التعقيم الأخرى.

التحقُّق

لا تتوفّر حاليًا اختبارات CTS مخصّصة لتنظيف تجاوز سعة الأعداد الصحيحة. بدلاً من ذلك، تأكَّد من اجتياز اختبارات CTS مع تفعيل IntSan أو بدون تفعيله للتحقّق من أنّها لا تؤثّر في الجهاز.

تنظيف الحدود

تضيف أداة BoundsSanitizer (BoundSan) أدوات إلى الملفات الثنائية لإدراج عمليات التحقّق من الحدود حول عمليات الوصول إلى المصفوفات. تتم إضافة عمليات التحقّق هذه إذا لم يتمكّن المترجم من إثبات أنّ الوصول سيكون آمنًا في وقت الترجمة، وإذا كان سيتم تحديد حجم المصفوفة في وقت التشغيل، وذلك حتى يمكن التحقّق من صحة الحجم. يتم نشر BoundSan في نظام التشغيل Android 10 في البلوتوث وبرامج الترميز. يوفّر المترجم BoundSan ويتم تفعيله تلقائيًا في العديد من المكوّنات على مستوى المنصة.

التنفيذ

يستخدم BoundSan أداة تنظيف الحدود UBSan. يتم تفعيل إجراء التخفيف هذا على مستوى كل وحدة. ويساعد هذا الإعداد في الحفاظ على أمان المكوّنات المهمة في Android، لذا لا يجب إيقافه.

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

تفعيل BoundSan في ملفات المخططات

يمكن تفعيل BoundSan في ملفات المخطط عن طريق إضافة "bounds" إلى السمة misc_undefined sanitize لوحدات المكتبة والثنائية:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
diag

تتيح السمة diag تفعيل وضع التشخيص لأدوات التنظيف. استخدِم وضع التشخيص أثناء الاختبار فقط. لا يتم إيقاف وضع التصحيح عند حدوث تجاوزات، ما يلغي ميزة الأمان التي توفّرها عملية التخفيف ويؤدي إلى زيادة الحمل على الأداء، لذا لا يُنصح باستخدامه في الإصدارات المخصّصة للإنتاج.

قائمة الحظر

تتيح السمة BLOCKLIST تحديد ملف BLOCKLIST يمكن للمطوّرين استخدامه لمنع تنظيف الدوال وملفات المصدر. لا تستخدِم هذه السمة إلا إذا كان الأداء يمثّل مشكلة وكانت الملفات أو الدوال المستهدَفة تساهم بشكل كبير في ذلك. راجِع هذه الملفات/الدوال يدويًا للتأكّد من أنّ عمليات الوصول إلى المصفوفات آمنة. يمكنك الاطّلاع على تحديد المشاكل وحلّها لمعرفة المزيد من التفاصيل.

تفعيل BoundSan في ملفات makefile

يمكن تفعيل BoundSan في ملفات makefile من خلال إضافة "bounds" إلى المتغيّر LOCAL_SANITIZE لوحدات المكتبة والثنائية:

    LOCAL_SANITIZE := bounds
    # Optional features
    LOCAL_SANITIZE_DIAG := bounds
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt

يقبل LOCAL_SANITIZE قائمة أدوات تنظيف مفصولة بفاصلة.

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

تسمح LOCAL_SANITIZE_BLOCKLIST بتحديد ملف BLOCKLIST يسمح للمطوّرين بمنع تنظيف الدوال وملفات المصدر. لا تستخدِم هذه السمة إلا إذا كان الأداء يمثّل مشكلة وكانت الملفات أو الدوال المستهدَفة تساهم بشكل كبير في ذلك. راجِع هذه الملفات/الدوال يدويًا للتأكّد من أنّ عمليات الوصول إلى المصفوفات آمنة. يمكنك الاطّلاع على تحديد المشاكل وحلّها لمعرفة المزيد من التفاصيل.

إيقاف BoundSan

يمكنك إيقاف BoundSan في الدوال وملفات المصدر باستخدام BLOCKLISTs أو سمات الدوال. من الأفضل إبقاء BoundSan مفعَّلاً، ولا يجب إيقافه إلا إذا كانت الدالة أو الملف يتسبّبان في زيادة كبيرة في الحمل الزائد للأداء وتمت مراجعة المصدر يدويًا.

للحصول على مزيد من المعلومات حول إيقاف BoundSan باستخدام سمات الدالة وتنسيق ملف القائمة المحظورة، يُرجى الرجوع إلى مستندات Clang LLVM. يمكنك حصر عملية إضافة BLOCKLIST في أداة التنظيف المحدّدة من خلال استخدام أسماء أقسام تحدّد أداة التنظيف المستهدَفة لتجنُّب التأثير في أدوات التنظيف الأخرى.

التحقُّق

لا تتوفّر اختبارات CTS مخصّصة لـ BoundSan. بدلاً من ذلك، تأكَّد من اجتياز اختبارات CTS سواء كانت ميزة BoundSan مفعّلة أو غير مفعّلة للتحقّق من أنّها لا تؤثّر في الجهاز.

تحديد المشاكل وحلّها

اختبِر المكوّنات بدقة بعد تفعيل BoundSan للتأكّد من معالجة أي عمليات وصول خارج الحدود لم يتم رصدها سابقًا.

يمكن التعرّف بسهولة على أخطاء BoundSan لأنّها تتضمّن رسالة الإيقاف التالية:

    pid: ###, tid: ###, name: Binder:###  >>> /system/bin/foobar <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: out-of-bounds'

عند التشغيل في وضع التشخيص، تتم طباعة الملف المصدر ورقم السطر وقيمة الفهرس في logcat. لا يعرض هذا الوضع رسالة إيقاف تلقائيًا. راجِع logcat للتحقّق من عدم حدوث أي أخطاء.

    external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'