По состоянию на 2016 год около 86% всех уязвимостей в Android связаны с безопасностью памяти. Большинство уязвимостей эксплуатируются злоумышленниками, которые изменяют обычный поток управления приложения для выполнения произвольных вредоносных действий со всеми привилегиями эксплуатируемого приложения. Целостность потока управления (CFI) — это механизм безопасности, который запрещает вносить изменения в исходный график потока управления скомпилированного двоичного файла, что значительно затрудняет выполнение таких атак.
В Android 8.1 мы включили реализацию CFI LLVM в стеке мультимедиа. В Android 9 мы включили CFI в большем количестве компонентов, а также в ядре. Системный CFI включен по умолчанию, но вам нужно включить CFI ядра.
CFI LLVM требует компиляции с Link-Time Optimization (LTO) . LTO сохраняет представление биткода LLVM объектных файлов до времени компоновки, что позволяет компилятору лучше рассуждать о том, какие оптимизации могут быть выполнены. Включение LTO уменьшает размер конечного двоичного файла и повышает производительность, но увеличивает время компиляции. При тестировании на Android сочетание LTO и CFI приводит к незначительным накладным расходам на размер кода и производительность; в некоторых случаях улучшается и то, и другое.
Для получения дополнительных технических подробностей о CFI и о том, как обрабатываются другие проверки прямого контроля, см. Документацию LLVM Design .
Примеры и источник
CFI предоставляется компилятором и добавляет приборы в двоичный файл во время компиляции. Мы поддерживаем CFI в Clang Toolchain и системе сборки Android в AOSP.
CFI включен по умолчанию для устройств Arm64 для набора компонентов в /platform/build/target/product/cfi-common.mk
. Он также напрямую включен в наборе файлов makefiles/blueprint компонентов мультимедиа, таких как /platform/frameworks/av/media/libmedia/Android.bp
и /platform/frameworks/av/cmds/stagefright/Android.mk
.
Внедрение системы CFI
CFI включен по умолчанию, если вы используете Clang и систему сборки Android. Поскольку CFI помогает обеспечить безопасность пользователей Android, вы не должны отключать его.
На самом деле, мы настоятельно рекомендуем вам включить CFI для дополнительных компонентов. Идеальными кандидатами являются привилегированный собственный код или собственный код, который обрабатывает ненадежный пользовательский ввод. Если вы используете clang и систему сборки Android, вы можете включить CFI в новых компонентах, добавив несколько строк в файлы makefile или blueprint.
Поддержка CFI в makefiles
Чтобы включить CFI в файле MAKE, например, /platform/frameworks/av/cmds/stagefright/Android.mk
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 в файле Blueprint, например, /platform/frameworks/av/media/libmedia/Android.bp
av/media/libmedia/android.bp, добавить:
sanitize: { cfi: true, diag: { cfi: true, }, blacklist: "cfi_blacklist.txt", },
Поиск неисправностей
Если вы позволяете CFI в новых компонентах, вы можете столкнуться с несколькими проблемами с ошибками несоответствия типа функции и ошибками несоответствия кода сборки .
Ошибки несоответствия типов функций возникают из-за того, что CFI ограничивает косвенные вызовы переходом только к функциям, имеющим тот же динамический тип, что и статический тип, используемый в вызове. CFI ограничивает вызовы виртуальных и невиртуальных функций-членов переходом только к объектам, которые являются производным классом статического типа объекта, используемого для выполнения вызова. Это означает, что если у вас есть код, который нарушает любое из этих предположений, инструментарий, который добавляет CFI, прервется. Например, трассировка стека показывает SIGABRT, а logcat содержит строку о целостности потока управления, обнаружившей несоответствие.
Чтобы исправить это, убедитесь, что вызываемое функция имеет тот же тип, который был статически объявлен. Вот два примера CLS:
- Bluetooth : /c/platform/system/bt/+/532377
- NFC : /c/platform/system/nfc/+/527858
Другой возможной проблемой является попытка включить CFI в коде, который содержит косвенные вызовы в сборку. Поскольку код сборки не вводится, это приводит к несоответствию типа.
Чтобы исправить это, создайте оболочки собственного кода для каждого вызова сборки и дайте оболочкам ту же сигнатуру функции, что и вызывающему указателю. Затем оболочка может напрямую вызывать код сборки. Поскольку прямые ветвления не инструментируются CFI (они не могут быть повторно указаны во время выполнения и, таким образом, не представляют угрозы безопасности), это исправит проблему.
Если функций сборки слишком много и их невозможно исправить, вы также можете внести в черный список все функции, содержащие косвенные вызовы сборки. Это не рекомендуется, так как это отключает проверки CFI для этих функций, тем самым открывая поверхность атаки.
Отключение CFI
Мы не наблюдали никаких накладных расходов на производительность, поэтому вам не нужно отключать CFI. Однако, если есть влияние, с которым сталкивается пользователь, вы можете выборочно отключить CFI для отдельных функций или исходных файлов, предоставив файл черного списка санитайзера во время компиляции. Черный список предписывает компилятору отключить инструментарий CFI в указанных местах.
Система сборки Android обеспечивает поддержку черных списков для каждого компонента (позволяя вам выбирать исходные файлы или отдельные функции, которые не получат инструментарий CFI) как для Make, так и для Soong. Для получения более подробной информации о формате файла черного списка см. документацию по исходному коду Clang .
Проверка
В настоящее время нет теста CTS специально для CFI. Вместо этого убедитесь, что тесты CTS проходят с CFI или без него, чтобы убедиться, что CFI не влияет на устройство.