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

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

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

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

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

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

CFI предоставляется компилятором и добавляет инструменты в двоичный файл во время компиляции. Мы поддерживаем CFI в цепочке инструментов Clang и систему сборки 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 в новых компонентах, добавив несколько строк в свои 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 , добавьте:

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

Исправление проблем

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

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

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

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

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

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

Отключение CFI

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

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

Проверка

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