На базе Clang для языка Си реализован режим проверки границ буферов

Новости мира unix. Хотите узнать секрет вечного счастья? Откройте страницу 246.
Ответить
acolyte
Аватара пользователя
Сообщения: 3727
Зарегистрирован: 20.08.2022

#

На базе Clang для языка Си реализован режим проверки границ буферов
Дата публикации:Fri, 24 Jan 2025 14:22:13 +0300




Инженеры из компании Apple объявили о готовности для тестирования режима "-fbounds-safety" для компилятора Clang, предоставляющего гарантии безопасной работы с буферами в коде на языке Си. Режим включён в состав форка LLVM, поддерживаемого компанией Apple для проекта Swift. В дальнейшем запланирована постепенная передача функциональности "-fbounds-safety" в основную кодовую базу LLVM/Clang.




Отмечается, что предложенный механизм защиты уже активно применяется в продуктах Apple, таких как ядро XNU, прошивки, библиотеки для работы со звуком и декодировщики изображений. Включение режима "-fbounds-safety" снижает производительность приложений в среднем на 5% (разброс от -1% до 29%), увеличивает размер кода на 9.1% (разброс от -1.4% до 38%) и замедляет компиляцию на 11%.







Использование режима "-fbounds-safety" для автоматического выявления выхода за границы области памяти, связанной с указателем, требует добавления в код специальных аннотаций и включения заголовочного файла "ptrcheck.h". Суть предложенного метода защиты в автоматическом прикреплении проверок соблюдения допустимых границ, добавляемых на основе выставленных вручную аннотаций или известных компилятору размеров.



В отличие от использования в коде расширенных указателей (wide pointer), в которых кроме адреса имеются сведения о верхней и нижней границе буфера, использование режима "-fbounds-safety" не нарушает ABI (Application Binary Interface), не меняет формат экспортируемых указателей и не требует переработки сразу для всего проекта. В режиме "-fbounds-safety" расширенные указатели применяются только в областях, не пересекающихся с ABI, а для указателей, влияющих на ABI, применяются обычные указатели с подстановкой проверок, формируемых на основе аннотаций с информацией о границах.



Аннотации необходимо прикреплять к указателям в полях структур и параметрах функций, указывающих на массив объектов, а также к глобальным переменным с указателями. Для указателей в локальных переменных аннотации добавлять не нужно, так как они автоматически обрабатываются как расширенные указатели, уже включающие информацию о допустимых границах. Подсказки о конструкциях в коде, для которых требуется добавление аннотаций, выводятся компилятором при запуске с флагом "-fbounds-safety".



Модель защиты на основе "-fbounds-safety" можно внедрять постепенно, файл за файлом, не прерывая разработку всего проекта. Добавление защиты в проект сводится к указанию аннотаций в определённом файле с кодом, устранению предупреждений компилятора и проведению тестирования работы программы, после чего данные этапы повторяются для следующего файла. Код с добавленными аннотациями остаётся совместим с обычным Си-кодом и компиляторами, не поддерживающими "-fbounds-safety" (при сборке другими компиляторами или при сборке без флага "-fbounds-safety" просто не будут добавлены дополнительные проверки границ).




Во время работы программы, в случае выявления обращения за пределы допустимых границ, генерируется исключение и программа завершает своё выполнение. Аварийное завершение также может произойти при указании некорректных аннотаций, поэтому при использовании "-fbounds-safety" необходимо проведение дополнительного тестирования работы программы.



В примере ниже в параметр "int *p" вставлена аннотация "__counted_by(n)", добавляющая дополнительную проверку на допустимые границы, действующую во время выполнения. Если попытаться скомпилировать код в режиме "-fbounds-safety" без указания данной аннотации, компилятор выведет предупреждение об отсутствии информации о границах массива при обработке выражения "p = 0".




#include <ptrcheck.h>

void init_buf(int *__counted_by(n) p, int n) {
for (int i = 0; i < n; ++i)
p = 0; // в режиме "-fbounds-safety" компилятор сам подставит проверку, аналогичную коду "if (i < 0 || i >= n) trap();"
}




Для локальных переменных с указателями проверки прикрепляются автоматически, например:


void foo(int i){
char *buf = (char *)malloc(10); // для указателя buf будут сохранены сведения о границах
buf = 0xff; // будет автоматически подставлена проверка "if (buf + i < buf || buf + i >= buf + 10) trap();"
}




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

for (size_t i = 0; i < count; ++i) {
buf = i; // проверка "if (i < 0 || i >= count) trap()" добавлена не будет, так как выше уже имеется условие "i < count" и i не может быть меньше 0.
}




Основные аннотации:
  • "__counted_by(N)" - определяет размер буфера в элементах целевого типа.
    "__sized_by(N)" - определяет размер буфера в байтах.
    " __ended_by(P)" - задаёт верхнюю границу буфера.
    "__null_terminated" - учитывает нулевой символ в качестве конца буфера.
    "__single" - привязывает указатель к одному объекту. Применяется по умолчанию для указателей, влияющих на ABI, если явно не выставлена аннотация.
    "__bidi_indexable" - расширенный указатель с информацией о верхней и нижней границах. Применяется по умолчанию для указателей, не влияющих на ABI.
    "__indexable" - расширенный указатель с информацией о верхней границе.
    "__unsafe_indexable" - указатель без проверки границ (для переносимости с незащищённым кодом, например, для получения указателей из внешнего кода).




Новость позаимствована с opennet.ru
Ссылка на оригинал: https://www.opennet.ru/opennews/art.shtml?num=62606

Жизнь за Нер'зула!

Ответить