Отмечается, что предложенный механизм защиты уже активно применяется в продуктах 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[i] = 0».
#include ‹ptrcheck.h› void init_buf(int *__counted_by(n) p, int n) { for (int i = 0; i ‹ n; ++i) p[i] = 0; // в режиме "-fbounds-safety" компилятор сам подставит проверку, аналогичную коду "if (i = n) trap();" }
Для локальных переменных с указателями проверки прикрепляются автоматически, например:
void foo(int i){ char *buf = (char *)malloc(10); // для указателя buf будут сохранены сведения о границах buf[i] = 0xff; // будет автоматически подставлена проверка "if (buf + i = buf + 10) trap();" }
По возможности компилятор проводит оптимизацию и исключает добавление лишнего кода, если в коде уже имеются необходимые проверки. Например:
for (size_t i = 0; i = count) trap()" добавлена не будет, так как выше уже имеется условие "i
Основные аннотации:
- «__counted_by(N)» — определяет размер буфера в элементах целевого типа.
- «__sized_by(N)» — определяет размер буфера в байтах.
- » __ended_by(P)» — задаёт верхнюю границу буфера.
- «__null_terminated» — учитывает нулевой символ в качестве конца буфера.
- «__single» — привязывает указатель к одному объекту. Применяется по умолчанию для указателей, влияющих на ABI, если явно не выставлена аннотация.
- «__bidi_indexable» — расширенный указатель с информацией о верхней и нижней границах. Применяется по умолчанию для указателей, не влияющих на ABI.
- «__indexable» — расширенный указатель с информацией о верхней границе.
- «__unsafe_indexable» — указатель без проверки границ (для переносимости с незащищённым кодом, например, для получения указателей из внешнего кода).
Источник: http://www.opennet.ru/opennews/art.shtml?num=62606