UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) выполняет инструментарий на этапе компиляции для проверки различных типов неопределенного поведения. Хотя UBSan способен обнаруживать множество ошибок, связанных с неопределенным поведением , Android поддерживает:

  • выравнивание
  • буль
  • границы
  • перечисление
  • перелив
  • деление числа на ноль
  • деление целого числа на ноль
  • ненулевой атрибут
  • нулевой
  • возвращаться
  • возвращает ненулевой атрибут
  • смещение базы
  • показатель сдвига
  • переполнение знакового целого числа
  • недостижимый
  • беззнаковое целое-переполнение
  • vla-bound

Уязвимость unsigned-integer-overflow, хотя технически и не является неопределенным поведением, включена в санитайзер и используется во многих модулях Android, включая компоненты медиасервера, для устранения любых скрытых уязвимостей переполнения целых чисел.

Выполнение

В системе сборки 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 . Класс санитайзера integer можно использовать с 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 и не должна отключаться.

Мы настоятельно рекомендуем включить проверку на переполнение целочисленных значений (Integer Overflow Sanitization) для дополнительных компонентов. Идеальными кандидатами являются привилегированный нативный код или нативный код, обрабатывающий ненадежные пользовательские данные. Проверка на переполнение целочисленных значений сопряжена с небольшими накладными расходами на производительность, зависящими от объема используемого кода и частоты выполнения арифметических операций. Ожидайте небольшого увеличения накладных расходов и проверьте, не вызывает ли это проблем с производительностью.

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

Чтобы включить 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",
       },

Как и в случае с файлами make, свойство 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'

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

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

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

После обнаружения проблемной арифметической операции убедитесь, что переполнение является безобидным и преднамеренным (например, не имеет последствий для безопасности). Для устранения ошибки, приводящей к прерыванию работы санитайзера, можно использовать следующие действия:

  • Рефакторинг кода для предотвращения переполнения ( пример )
  • Переполнение явно осуществляется с помощью встроенных функций Clang __builtin_*_overflow ( пример ).
  • Отключение проверки данных в функции путем указания атрибута no_sanitize ( пример )
  • Отключение проверки целостности функции или исходного файла с помощью файла BLOCKLIST ( пример )

Следует использовать максимально детализированное решение. Например, в большой функции с множеством арифметических операций и одной операцией переполнения следует рефакторизовать именно эту операцию, а не всю функцию целиком, используя метод BLOCKLIST.

К распространённым сценариям, которые могут привести к безобидным переливам, относятся:

  • Неявные преобразования типов , при которых происходит переполнение беззнакового типа до преобразования в знаковый тип ( пример ).
  • Удаление элементов из связанного списка, при котором индекс цикла уменьшается при удалении ( пример ).
  • Присвоение значения -1 для беззнакового типа вместо указания фактического максимального значения ( пример ).
  • Циклы, которые уменьшают значение беззнакового целого числа в условии ( пример , пример ).

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

Отключить IntSan

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

Дополнительную информацию об отключении IntSan с помощью атрибутов функций и форматировании файлов BLOCKLIST см. в документации Clang. Использование BLOCKLIST следует ограничивать конкретным санитайзером, используя имена разделов, указывающие на целевой санитайзер, чтобы избежать влияния на другие санитайзеры.

Проверка

В настоящее время нет специальных тестов CTS для проверки целочисленной санитаризации переполнения (Integer Overflow Sanitization). Вместо этого убедитесь, что тесты CTS проходят успешно как с включенной, так и с выключенной функцией IntSan, чтобы проверить, не влияет ли она на работу устройства.

Санитаризация границ

BoundsSanitizer (BoundSan) добавляет в бинарные файлы инструменты для вставки проверок границ при доступе к массивам. Эти проверки добавляются, если компилятор не может доказать во время компиляции, что доступ будет безопасным, и если размер массива будет известен во время выполнения, чтобы его можно было проверить. Android 10 использует BoundSan в Bluetooth и кодеках. BoundSan предоставляется компилятором и включен по умолчанию в различных компонентах платформы.

Выполнение

BoundSan использует средство проверки границ UBSan . Эта мера защиты включается на уровне каждого модуля. Она помогает обеспечить безопасность критически важных компонентов Android и не должна отключаться.

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

Включите BoundSan в файлах чертежей.

Функция BoundSan может быть включена в файлах Blueprint путем добавления "bounds" к свойству sanitize параметра misc_undefined для бинарных и библиотечных модулей:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
диаг

Свойство diag включает диагностический режим для средств очистки данных. Используйте диагностический режим только во время тестирования. Диагностический режим не прерывает работу при переполнении, что сводит на нет преимущества защиты от ошибок и приводит к увеличению накладных расходов на производительность, поэтому его не рекомендуется использовать в производственных сборках.

БЛОК-СПИСОК

Свойство BLOCKLIST позволяет указать файл BLOCKLIST, который разработчики могут использовать для предотвращения проверки функций и исходных файлов. Используйте это свойство только в том случае, если производительность имеет значение и целевые файлы/функции вносят существенный вклад. Проведите ручную проверку этих файлов/функций, чтобы убедиться в безопасности доступа к массивам. Дополнительные сведения см. в разделе «Устранение неполадок» .

Включите BoundSan в make-файлах.

Включить BoundSan в make-файлах можно, добавив "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 в функциях и исходных файлах можно с помощью списков блоков (BLOCKLIST) или атрибутов функций. Лучше оставлять BoundSan включенным, поэтому отключайте его только в том случае, если функция или файл создают значительные накладные расходы на производительность, и исходный код был проверен вручную.

Для получения дополнительной информации об отключении BoundSan с помощью атрибутов функций и форматирования файла BLOCKLIST обратитесь к документации 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]'