Целостность потока управления

По состоянию на 2016 год около 86% всех уязвимостей Android связаны с безопасностью памяти. Большинство уязвимостей эксплуатируются злоумышленниками, изменяющими обычный поток управления приложением для выполнения произвольных вредоносных действий со всеми привилегиями эксплуатируемого приложения. Целостность потока управления (CFI) - это механизм безопасности, который запрещает вносить изменения в исходный граф потока управления скомпилированного двоичного файла, что значительно затрудняет выполнение таких атак.

В Android 8.1 мы включили реализацию CFI в LLVM в стеке мультимедиа. В Android 9 мы включили CFI в большем количестве компонентов, а также в ядре. Системный CFI включен по умолчанию, но вам необходимо включить CFI ядра.

CFI LLVM требует компиляции с оптимизацией времени соединения (LTO) . LTO сохраняет представление объектных файлов в виде битового кода LLVM до времени компоновки, что позволяет компилятору лучше понять, какие оптимизации можно выполнить. Включение LTO уменьшает размер финального двоичного файла и повышает производительность, но увеличивает время компиляции. При тестировании на Android комбинация LTO и CFI приводит к незначительным накладным расходам на размер кода и производительность; в некоторых случаях оба улучшились.

Для получения дополнительных технических подробностей о CFI и о том, как обрабатываются другие проверки прямого контроля, см. Проектную документацию LLVM .

Примеры и источник

CFI предоставляется компилятором и добавляет инструменты в двоичный файл во время компиляции. Мы поддерживаем CFI в цепочке инструментов Clang и систему сборки Android в AOSP.

CFI включен по умолчанию для устройств Arm64 для набора компонентов в /platform/build/target/product/cfi-common.mk . Он также напрямую включен в наборе файлов makefiles / /platform/frameworks/av/media/libmedia/Android.bp медиа-компонентов, таких как /platform/frameworks/av/media/libmedia/Android.bp и /platform/frameworks/av/cmds/stagefright/Android.mk .

Внедрение системы CFI

CFI включен по умолчанию, если вы используете Clang и систему сборки Android. Поскольку CFI помогает обезопасить пользователей Android, отключать его не следует.

Фактически, мы настоятельно рекомендуем вам включить CFI для дополнительных компонентов. Идеальные кандидаты - это привилегированный машинный код или машинный код, обрабатывающий ненадежный ввод пользователя. Если вы используете clang и систему сборки Android, вы можете включить CFI в новых компонентах, добавив несколько строк в свои make-файлы или файлы чертежей.

Поддержка CFI в make-файлах

Чтобы включить CFI в файле make, например /platform/frameworks/av/cmds/stagefright/Android.mk , добавьте:

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
  • LOCAL_SANITIZE указывает CFI как дезинфицирующее средство во время сборки.
  • LOCAL_SANITIZE_DIAG включает режим диагностики для CFI. В режиме диагностики во время сбоев в logcat выводится дополнительная информация об отладке, что полезно при разработке и тестировании ваших сборок. Однако не забудьте удалить диагностический режим на производственных сборках.
  • LOCAL_SANITIZE_BLACKLIST позволяет компонентам выборочно отключать инструментарий CFI для отдельных функций или исходных файлов. Вы можете использовать черный список в качестве последнего средства, чтобы исправить любые проблемы, с которыми сталкивается пользователь, которые в противном случае могли бы существовать. Для получения дополнительной информации см. Отключение CFI .

Поддержка CFI в файлах чертежей

Чтобы включить CFI в файле /platform/frameworks/av/media/libmedia/Android.bp , например /platform/frameworks/av/media/libmedia/Android.bp , добавьте:

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

Поиск проблемы

Если вы включаете CFI в новых компонентах, вы можете столкнуться с несколькими проблемами, связанными с ошибками несоответствия типа функции и ошибками несоответствия типа кода сборки .

Ошибки несоответствия типа функции возникают из-за того, что CFI ограничивает косвенные вызовы только переходом к функциям, которые имеют тот же динамический тип, что и статический тип, используемый в вызове. CFI ограничивает вызовы виртуальных и не виртуальных функций-членов только переходом к объектам, которые являются производным классом статического типа объекта, используемого для выполнения вызова. Это означает, что если у вас есть код, который нарушает любое из этих предположений, добавление инструментов CFI будет прервано. Например, трассировка стека показывает SIGABRT, а logcat содержит строку о целостности потока управления, обнаруживающую несоответствие.

Чтобы исправить это, убедитесь, что вызываемая функция имеет тот же тип, который был объявлен статически. Вот два примера CL:

Другая возможная проблема - попытка включить CFI в коде, который содержит косвенные вызовы сборки. Поскольку код сборки не типизирован, это приводит к несоответствию типов.

Чтобы исправить это, создайте оболочки собственного кода для каждого вызова сборки и дайте оболочкам ту же сигнатуру функции, что и указатель вызова. Затем оболочка может напрямую вызывать код сборки. Поскольку прямые переходы не инструментируются CFI (они не могут быть повторно указаны во время выполнения и поэтому не представляют угрозы безопасности), это решит проблему.

Если функций сборки слишком много и все они не могут быть исправлены, вы также можете занести в черный список все функции, которые содержат косвенные вызовы сборки. Это не рекомендуется, поскольку отключает проверки CFI для этих функций, тем самым открывая поверхность для атаки.

Отключение CFI

Мы не наблюдали никаких накладных расходов на производительность, поэтому отключать CFI не нужно. Однако, если есть влияние на пользователя, вы можете выборочно отключить CFI для отдельных функций или исходных файлов, предоставив файл черного списка sanitizer во время компиляции. Черный список инструктирует компилятор отключить инструменты CFI в указанных местах.

Система сборки Android обеспечивает поддержку черных списков компонентов (позволяющих выбирать исходные файлы или отдельные функции, которые не будут получать инструменты CFI) как для Make, так и для Soong. Для получения дополнительных сведений о формате файла черного списка см. Документацию Clang .

Проверка

В настоящее время нет теста CTS специально для CFI. Вместо этого убедитесь, что тесты CTS проходят с включенным CFI или без него, чтобы убедиться, что CFI не влияет на устройство.