Что делать со срабатыванием -Werror=clobbered?
TODO: навесить volatile. Возможно FP.
Если однотипных ошибок [-Werror=…] много, для
продолжения тестирования можно эмулировать поведение безопасного
компилятора второго класса без предупреждения, провоцирующего эти
ошибки. Для этого можно воспользоваться опцией
--print-safe-options=2. Компилятор
в таком режиме не является безопасным
компилятором второго класса.
Что делать со срабатыванием -fsanitize=function на OpenSSL?
Срабатывания этого санитайзера на OpenSSL мы считаем истинными. TODO: пример, почему.
Текущие версии SAFEC допускают отключение этого
санитайзера: -Safe1 -fno-sanitize=function.
Верно ли, что -SafeN соответствует требованиям
для уровня доверия M?
Нет. Соотнесения между классами защищённости компилятора и УД на данный момент нет. Сейчас идёт только апробация компилятора в индустрии.
Почему программа успешно собралась SAFEC, но при запуске завершилась без диагностики?
Завершение программ без вывода отладочной информации при возникновении ошибочных ситауций выбрано намеренно. Печать отладночного сообщения раскрывает информацию о программе, которая потенциально может быть использована злоумышленником. В частности, это верно для старых версий Glibc.
Сам код, печатающий такие сообщения, может не быть предназначен для использования в контексте, где важна безопасность. Контекст предполагаемого его использования — это отладка. Чтобы не увеличивать поверхность атаки, безопасный компилятор избегает вызова этого кода.
Ещё один потенциально нежелательный сценарий — вызов
функции abort().
Эта функция посылает процессу
сигнал SIGABRT,
для которого действие по умолчанию (signal disposition) —
завершить процесс и записать образ памяти процесса
в core
dump файл. Одна из потенциальных угроз при неадекватной
настройке окружения — это отказ в обслуживании из-за
потребления этими файлами избыточного дискового пространства.
Убедиться, что программа была завершена в результате возникновения ошибочной ситуации, можно по коду выхода программы: по умолчанию для программ, собранных SAFEC, это 255.
Опции компилятора, включающие проверки времени выполнения, которые могут спровоцировать аварийное завершение:
-D_FORTIFY_SOURCE=2(все-Safe);-fstack-protector(все-Safe);- санитайзеры (
-Safe1: см. список в документации).
Как отлаживать аварийные завершения программ?
Включить отладочный вывод можно с помощью специальных опций компиляции.
Как отлаживать аварийный останов из-за -D_FORTIFY_SOURCE=2?
Установка значения макроса _FORTIFY_ABORT с помощью
-D_FORTIFY_ABORT поменяет поведение на
вызов abort().
Как отлаживать аварийный останов из-за -fstack-protector?
-fno-stack-protector-exit-on-error— изменить поведение на вызов вызов библиотечной функции__stack_chk_fail()(поведение базового GCC).-fstack-protector-abort-on-error— изменить поведение на вызовabort().
Как отлаживать аварийный останов из-за срабатывания санитайзеров?
Способ первый:
-
передать
компилятору
-fno-sanitize-undefined-exit-on-errorдля вызова специфичных для конкретных случаев неопределённого поведения встроенных функций и функций библиотеки libubsan (поведение базового GCC); и - выставить значение переменной
окружения
UBSAN_OPTIONSпри запуске программы:UBSAN_OPTIONS=halt_on_error=1,print_stacktrace=true,symbolize=true.
Способ второй: изменить поведение на вызов abort() с
помощью -fsanitize-undefined-abort-on-error.
Особенности сборки ядра Linux
Поскольку ядра ОС работают в окружении, отличном от userspace программ, для них применяются специальные режимы компиляции.
В этих случаях некоторые из функций безопасности, включаемых безопасным компилятором для userspace программ, не будут гарантированы. Кроме этого, некоторые сценарии аварийного завершения, применяемые безопасным компилятором по умолчанию, в контексте ядра приводят к нежелательным результатам.
Чтобы избежать снижения уровня безопасности и не спровоцировать проблемы времени выполнения, требуется адаптировать конфигурацию сборки.
-Safe3
- Добавить
KCFLAGS += -O2 -Safe3 -fno-stack-protector-exit-on-error. - Установить значение конфигурационного
параметра
CONFIG_FORTIFY_SOURCE=y. Это позволяет использовать механизм защиты от переполнения буфера постоянного размера при использовании стандартных функций работы со стороками и памятью, реализованный в Linux. Заголовочные файлы fortify headers, реализующие эту защиту в SAFEC, при сборке ядра игнорируются (режим-nostdinc).
-Safe2
Аналогично -Safe3. Заменить -Safe3
на -Safe2.
-Safe1
На данный момент не поддерживается. SAFEC не позволяет достаточно
тонко настраивать поведение при срабатывании санитайзеров (WIP), в
результате чего в контексте ядра оно безальтернативно приводит
к kernel panic. В качестве замены рекомендуем подключать
санитайзеры с помощью конфигурационного
параметра CONFIG_UBSAN, как описано в
linux/Documentation/dev-tools/ubsan.rst.
Какие системные требования имеют сборки безопасного компилятора?
Native сборки компилятора для x86_64 скомпонованы относительно Glibc 2.27. Поэтому версия системной Glibc должна быть не ниже этой версии.
Проверить версию можно с помощью ldd --version.
В чём разница между cconly и addon-toolchain сборками?
Сборки cconly содержат только компилятор и
библиотеки, собираемые из исходного кода GCC. Они предназначены
для использования вместе с системной libc.
Сборки cconly безопасного компилятора соответствуют
классам защищённости 2 и 3 по ГОСТ Р 71206-2024. Классу
защищённости 1 они соответсвуют за одним исключением: нет функции
уникального (от запуска к запуску) распределения в памяти
машинного кода функций, т.к. нет поддержки со стороны
динамического загрузчика. В качестве альтернативы доступна
функция статического — т.е. выполняемого на
этапе компиляции — уникального распределения.
Это допускается требованиями для окружений, где
реализация поддержки в динамическом загрузчике невозможна.
Сборки addon-toolchain помимо компилятора также
содержат binutils и glibc.
Использование addon-toolchain сборки в качестве
системного тулчейна не предполагается. Эти
сборки — тестовые. Они демонстрируют
возможность реализации всех требований стандарта: соответствуют
классам защищённости 1, 2, 3.
Эти сборки требуют установки в /opt. Из-за этого
сборка ПО с зависимостями требует дополнительных шагов, которые
описаны в пользовательской документации. А если собранное ПО
распространять, нужно было бы учесть, что программы будут
ссылаться на некоторые библиотеки в
подкаталогах /opt.
Какую версию безопасного компилятора использовать?
Мы рекомендуем использовать версию безопасного компилятора, соответствующую major версии системного, по следующим причинам.
-
Для корректности сравнения результатов, выдаваемых обычным компилятором и безопасным. Это может стать актуально, если по результатам использования потребуется поддержка с нашей стороны: например, если наш компилятор выдаст неожиданную ошибку. Для упрощения процесса мы исключаем одну переменную — версию.
-
Если использовать безопасный компилятор более новой версии, чем GCC, установленный в системе, может возникнуть проблема при загрузке результирующих программ. Дело в том, что вместе с компилятором также поставляются библиотеки — например, libstdc++ и libubsan.
При этом libstdc++ может оказаться в системе даже если там нет компилятора: достаточно любого ПО, разработанного на C++ с использованием стандартной библиотеки. Поскольку обычно применяется динамическая линковка, пакет с таким ПО потребует установки пакета с libstdc++ в качестве зависимости.
Если не предпринимать дополнительных шагов, во время компоновки будет использована библиотека из поставки нашего компилятора, а во время загрузки — системная. Если в них окажутся разные наборы (или версии) символов и при компоновке будет использован более новый символ, то при запуске программы загрузчик не сможет его найти в более старой версии библиотеки.
Используя одинаковую версию мы обеспечиваем совместимость. Это не единственный способ, но он простой и прозрачный для пользователя.
При этом переход более новую minor версию (после точки) не должен приводить к несовместимости. Разница между такими версиями в базовом компиляторе — только в устранении ошибок. Поэтому, например, переход с GCC 11.2 на SAFEC 11.4 не должен приводить к регрессиям, которые можно было бы отнести к переходу на более новую версию компилятора.
В чём разница версий safecN
и safe-N.M?
Разная схема именования архивов (safecN и safe-N.M) связана с процессом релиза GCC и не влияет на использование.
Мы называем версии, основанные на ветках GCC, не получающих
обновлений, "safecN". Например, версия safec10
основана на GCC 10.5. При этом известно, что
ветка releases/gcc-10 закрыта. Это значит, что релиза
GCC 10.6 не будет.
Для версий SAFEC, основанных на активных ветках GCC,
указывается релиз, взятый за
основу. Например, safe-11.4. После выхода релиза GCC
11.5 мы выпустим версию safec11 на его основе,
которая заменит safe-11.4.
Сборка проекта с помощью addon-toolchain через CMake падает с
undefined reference, что делать?
Проблема может возникать из-за использования в скриптах
CMake команды find_library, которая не учитывает
что в addon-toolchain стандартные библиотеки расположены
в директории /opt/safec.
Эта проблема решается, например, с помощью добавления в переменную
окружения CMAKE_LIBRARY_PATH пути
/opt/safec/usr/lib64. Подробнее см.
документацию по find_library, где также
перечислены другие способы повлиять на её поведение.