UndefinedBehaviorSanitizer

ابزار UndefinedBehaviorSanitizer (UBSan) در زمان کامپایل، ابزارهایی را برای بررسی انواع مختلف رفتارهای تعریف نشده اجرا می‌کند. در حالی که UBSan قادر به تشخیص بسیاری از اشکالات رفتاری تعریف نشده است، اندروید از موارد زیر پشتیبانی می‌کند:

  • هم‌ترازی
  • بول
  • مرزها
  • شمارشی
  • شناور-ریخته‌گر-سرریز
  • اعشار-تقسیم بر صفر
  • تقسیم عدد صحیح بر صفر
  • ویژگی غیر تهی
  • تهی
  • بازگشت
  • ویژگی غیر تهی را برمی‌گرداند
  • پایه شیفت
  • توان شیفت
  • سرریز عدد صحیح علامت‌دار
  • دست نیافتنی
  • سرریز عدد صحیح بدون علامت
  • vla-bound

رفتار unsigned-integer-overflow، اگرچه از نظر فنی تعریف‌نشده نیست، اما در sanitizer گنجانده شده و در بسیاری از ماژول‌های اندروید، از جمله اجزای mediaserver، برای از بین بردن هرگونه آسیب‌پذیری سرریز عدد صحیح پنهان استفاده می‌شود.

پیاده‌سازی

در سیستم ساخت اندروید، می‌توانید 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

اندروید همچنین دو میانبر، integer و default-ub ، برای فعال کردن مجموعه‌ای از sanitizerها به طور همزمان دارد. 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 . کلاس sanitizer عدد صحیح را می‌توان با SANITIZE_TARGET و LOCAL_SANITIZE استفاده کرد، در حالی که default-ub فقط با SANITIZE_TARGET قابل استفاده است.

گزارش خطای بهتر

پیاده‌سازی پیش‌فرض UBSan در اندروید، هنگام مواجهه با رفتار نامشخص، یک تابع مشخص را فراخوانی می‌کند. به‌طور پیش‌فرض، این تابع لغو می‌شود. با این حال، از اکتبر ۲۰۱۶، UBSan در اندروید دارای یک کتابخانه زمان اجرای اختیاری است که گزارش خطای دقیق‌تری، از جمله نوع رفتار نامشخص مشاهده شده، اطلاعات فایل و خط کد منبع، ارائه می‌دهد. برای فعال کردن این گزارش خطا با بررسی اعداد صحیح، موارد زیر را به فایل 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 را برای مقاوم‌سازی چارچوب رسانه در اندروید ۷.۰ اضافه کردیم. در اندروید ۹، ما UBSan را گسترش دادیم تا اجزای بیشتری را پوشش دهد و پشتیبانی سیستم ساخت را برای آن بهبود بخشیدیم.

این قابلیت برای افزودن بررسی‌هایی پیرامون عملیات/دستورالعمل‌های محاسباتی - که ممکن است سرریز شوند - طراحی شده است تا در صورت وقوع سرریز، فرآیند به طور ایمن متوقف شود. این پاک‌کننده‌ها می‌توانند کل دسته از آسیب‌پذیری‌های فساد حافظه و افشای اطلاعات را که علت اصلی آنها سرریز عدد صحیح است، مانند آسیب‌پذیری اصلی Stagefright، کاهش دهند.

مثال‌ها و منابع

پاکسازی سرریز عدد صحیح (IntSan) توسط کامپایلر ارائه می‌شود و در طول زمان کامپایل، ابزارهایی را به فایل باینری اضافه می‌کند تا سرریزهای حسابی را تشخیص دهد. این قابلیت به طور پیش‌فرض در اجزای مختلف در سراسر پلتفرم، به عنوان مثال /platform/external/libnl/Android.bp ، فعال است.

پیاده‌سازی

IntSan از پاک‌کننده‌های سرریز عدد صحیح امضا شده و نشده‌ی UBSan استفاده می‌کند. این راهکار در سطح هر ماژول فعال می‌شود. این راهکار به ایمن نگه داشتن اجزای حیاتی اندروید کمک می‌کند و نباید غیرفعال شود.

ما اکیداً توصیه می‌کنیم که برای اجزای اضافی، پاکسازی سرریز عدد صحیح (Integer Overflow Sanitization) را فعال کنید. گزینه‌های ایده‌آل، کد بومی دارای امتیاز بالا یا کد بومی‌ای هستند که ورودی‌های غیرقابل اعتماد کاربر را تجزیه می‌کنند. سربار عملکردی کمی مرتبط با پاکسازی وجود دارد که به میزان استفاده از کد و میزان رواج عملیات حسابی بستگی دارد. انتظار درصد سربار کمی را داشته باشید و در صورت نگرانی در مورد عملکرد، آن را آزمایش کنید.

پشتیبانی از 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 مجموعه‌ای از گزینه‌های از پیش بسته‌بندی‌شده برای ضدعفونی‌کننده‌های سرریز عدد صحیح امضا شده و نشده با یک BLOCKLIST پیش‌فرض است.
  • 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 مجموعه‌ای از گزینه‌های از پیش بسته‌بندی‌شده برای پاک‌کننده‌های سرریز عدد صحیح امضا شده و نشده با یک BLOCKLIST پیش‌فرض است.

مجموعه ویژگی‌های 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 با پیام‌های Abort باشید که نشان‌دهنده‌ی سرریز شناسایی‌شده توسط UBSan هستند، مانند:

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

ردیابی پشته باید شامل تابعی باشد که باعث لغو اجرای برنامه شده است، با این حال، سرریزهایی که در توابع درون‌خطی رخ می‌دهند ممکن است در ردیابی پشته آشکار نباشند.

برای تعیین آسان‌تر علت اصلی، قابلیت تشخیص خطا (diagnostics) را در کتابخانه‌ای که باعث لغو فرآیند می‌شود، فعال کنید و سعی کنید خطا را دوباره ایجاد کنید. با فعال بودن قابلیت تشخیص، فرآیند متوقف نمی‌شود و در عوض به اجرای خود ادامه می‌دهد. متوقف نکردن فرآیند به حداکثر رساندن تعداد سرریزهای بی‌خطر در یک مسیر اجرای خاص، بدون نیاز به کامپایل مجدد پس از رفع هر اشکال، کمک می‌کند. قابلیت تشخیص خطا، یک پیام خطا تولید می‌کند که شامل شماره خط و فایل منبعی است که باعث لغو فرآیند شده است:

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')

پس از شناسایی عملیات محاسباتی مشکل‌ساز، مطمئن شوید که سرریز بی‌خطر و هدفمند است (مثلاً هیچ پیامد امنیتی ندارد). می‌توانید با انجام مراحل زیر، مشکل لغو sanitizer را برطرف کنید:

  • اصلاح کد برای جلوگیری از سرریز ( مثال )
  • سرریز صریح از طریق توابع __builtin_*_overflow از Clang ( مثال )
  • غیرفعال کردن sanitization در تابع با مشخص کردن ویژگی no_sanitize ( مثال )
  • غیرفعال کردن پاکسازی یک تابع یا فایل منبع از طریق یک فایل BLOCKLIST ( مثال )

شما باید از جزئی‌ترین راه‌حل ممکن استفاده کنید. برای مثال، یک تابع بزرگ با عملیات حسابی زیاد و یک عملیات سرریزکننده، باید به جای اینکه کل تابع را در لیست سیاه قرار دهد، آن عملیات واحد را بازسازی کند.

الگوهای رایجی که ممکن است منجر به سرریزهای خوش‌خیم شوند عبارتند از:

  • تبدیل‌های ضمنی که در آن‌ها یک سرریز بدون علامت قبل از تبدیل به یک نوع علامت‌دار رخ می‌دهد ( مثال )
  • حذف لیست پیوندی که در هنگام حذف، اندیس حلقه را کاهش می‌دهد ( مثال )
  • اختصاص دادن یک نوع بدون علامت به -1 به جای مشخص کردن حداکثر مقدار واقعی ( مثال )
  • حلقه‌هایی که یک عدد صحیح بدون علامت را در شرط ( مثال ، مثال ) کاهش می‌دهند

توصیه می‌شود توسعه‌دهندگان قبل از غیرفعال کردن پاکسازی، اطمینان حاصل کنند که مواردی که پاکسازی‌کننده سرریز را تشخیص می‌دهد، واقعاً بی‌خطر است و هیچ عوارض جانبی ناخواسته یا پیامدهای امنیتی ندارد.

غیرفعال کردن IntSan

شما می‌توانید IntSan را با استفاده از BLOCKLISTها یا ویژگی‌های تابع غیرفعال کنید. غیرفعال کردن را به صورت محدود و فقط زمانی انجام دهید که بازسازی کد غیرمنطقی باشد یا سربار عملکردی مشکل‌ساز وجود داشته باشد.

برای اطلاعات بیشتر در مورد غیرفعال کردن IntSan با ویژگی‌های تابع و قالب‌بندی فایل BLOCKLIST، به مستندات بالادستی Clang مراجعه کنید. BLOCKLISTing باید با استفاده از نام بخش‌هایی که ضدعفونی‌کننده هدف را مشخص می‌کنند، به ضدعفونی‌کننده خاص محدود شود تا از تأثیر بر سایر ضدعفونی‌کننده‌ها جلوگیری شود.

اعتبارسنجی

در حال حاضر، هیچ آزمایش CTS مخصوصی برای پاکسازی سرریز اعداد صحیح وجود ندارد. در عوض، مطمئن شوید که آزمایش‌های CTS با یا بدون فعال بودن IntSan با موفقیت انجام می‌شوند تا تأیید شود که دستگاه را تحت تأثیر قرار نمی‌دهد.

پاکسازی مرزها

BoundsSanitizer (BoundSan) ابزاری را به فایل‌های باینری اضافه می‌کند تا بررسی‌های مرزی را در مورد دسترسی‌های آرایه انجام دهد. این بررسی‌ها در صورتی اضافه می‌شوند که کامپایلر نتواند در زمان کامپایل ثابت کند که دسترسی ایمن خواهد بود و اگر اندازه آرایه در زمان اجرا مشخص باشد، بتوان آن را بررسی کرد. اندروید 10 از BoundSan در بلوتوث و کدک‌ها استفاده می‌کند. BoundSan توسط کامپایلر ارائه می‌شود و به طور پیش‌فرض در اجزای مختلف در سراسر پلتفرم فعال است.

پیاده‌سازی

BoundSan از ابزار سنجش مرزهای UBSan استفاده می‌کند. این راهکار در سطح هر ماژول فعال می‌شود. این راهکار به ایمن نگه داشتن اجزای حیاتی اندروید کمک می‌کند و نباید غیرفعال شود.

ما اکیداً توصیه می‌کنیم که BoundSan را برای اجزای اضافی فعال کنید. گزینه‌های ایده‌آل، کد بومی ممتاز یا کد بومی پیچیده‌ای هستند که ورودی‌های غیرقابل اعتماد کاربر را تجزیه می‌کنند. سربار عملکردی مرتبط با فعال کردن BoundSan به تعداد دسترسی‌های آرایه‌ای که نمی‌توان امنیت آنها را اثبات کرد، بستگی دارد. به طور متوسط ​​انتظار درصد سربار کمی را داشته باشید و اگر عملکرد نگران‌کننده است، آن را آزمایش کنید.

فعال کردن BoundSan در فایل‌های طرح اولیه

BoundSan را می‌توان در فایل‌های طرح با اضافه کردن "bounds" به ویژگی misc_undefined sanitize برای ماژول‌های باینری و کتابخانه‌ای فعال کرد:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
دیاگ

ویژگی diag حالت تشخیص را برای پاک‌کننده‌ها فعال می‌کند. فقط در طول آزمایش از حالت تشخیص استفاده کنید. حالت تشخیص در صورت سرریز شدن متوقف نمی‌شود، که این امر مزیت امنیتی کاهش را خنثی می‌کند و سربار عملکرد بالاتری را به همراه دارد، بنابراین برای ساخت‌های عملیاتی توصیه نمی‌شود.

لیست سیاه

ویژگی BLOCKLIST امکان تعیین مشخصات یک فایل BLOCKLIST را فراهم می‌کند که توسعه‌دهندگان می‌توانند از آن برای جلوگیری از پاکسازی توابع و فایل‌های منبع استفاده کنند. از این ویژگی فقط در صورتی استفاده کنید که عملکرد مورد توجه باشد و فایل‌ها/توابع مورد نظر سهم قابل توجهی داشته باشند. این فایل‌ها/توابع را به صورت دستی بررسی کنید تا از ایمن بودن دسترسی به آرایه اطمینان حاصل شود. برای جزئیات بیشتر به بخش عیب‌یابی مراجعه کنید.

فعال کردن BoundSan در makefiles

BoundSan را می‌توان با اضافه کردن "bounds" به متغیر LOCAL_SANITIZE برای ماژول‌های باینری و کتابخانه‌ای، در فایل‌های makefile فعال کرد:

    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 را در توابع و فایل‌های منبع با BLOCKLISTها یا ویژگی‌های تابع غیرفعال کنید. بهتر است BoundSan را فعال نگه دارید، بنابراین فقط در صورتی آن را غیرفعال کنید که تابع یا فایل، سربار عملکردی زیادی ایجاد کند و منبع به صورت دستی بررسی شده باشد.

برای اطلاعات بیشتر در مورد غیرفعال کردن BoundSan با ویژگی‌های تابع و قالب‌بندی فایل BLOCKLIST ، به مستندات Clang LLVM مراجعه کنید. با استفاده از نام بخش‌هایی که ضدعفونی‌کننده هدف را مشخص می‌کنند، BLOCKLISTing را به ضدعفونی‌کننده خاص محدود کنید تا از تأثیر بر سایر ضدعفونی‌کننده‌ها جلوگیری شود.

اعتبارسنجی

هیچ تست CTS مخصوصی برای BoundSan وجود ندارد. در عوض، مطمئن شوید که تست‌های CTS با یا بدون فعال بودن BoundSan با موفقیت انجام می‌شوند تا تأیید شود که دستگاه را تحت تأثیر قرار نمی‌دهد.

عیب‌یابی

پس از فعال‌سازی BoundSan، اجزا را به‌طور کامل آزمایش کنید تا مطمئن شوید که هرگونه دسترسی خارج از محدوده که قبلاً شناسایی نشده بود، مورد توجه قرار گرفته است.

خطاهای BoundSan را می‌توان به راحتی شناسایی کرد زیرا شامل پیام لغو tombstone زیر هستند:

    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]'