-Werror=clobbered
TODO: навесить volatile. Возможно FP.
Если однотипных ошибок [-Werror=…] много, для продолжения тестирования можно эмулировать поведение безопасного компилятора второго класса без предупреждения, провоцирующего эти ошибки. Для этого можно воспользоваться опцией --print-safe-options=2. Компилятор в таком режиме не является безопасным компилятором второго класса.
[-Werror=…]
--print-safe-options=2
-fsanitize=function
Срабатывания этого санитайзера на OpenSSL мы считаем истинными. TODO: пример, почему.
Текущие версии SAFEC допускают отключение этого санитайзера: -Safe1 -fno-sanitize=function.
-Safe1 -fno-sanitize=function
-SafeN
Нет. Соотнесения между классами защищённости компилятора и УД на данный момент нет. Сейчас идёт только апробация компилятора в индустрии.
Завершение программ без вывода отладочной информации при возникновении ошибочных ситауций выбрано намеренно. Печать отладночного сообщения раскрывает информацию о программе, которая потенциально может быть использована злоумышленником. В частности, это верно для старых версий Glibc.
Сам код, печатающий такие сообщения, может не быть предназначен для использования в контексте, где важна безопасность. Контекст предполагаемого его использования — это отладка. Чтобы не увеличивать поверхность атаки, безопасный компилятор избегает вызова этого кода.
Ещё один потенциально нежелательный сценарий — вызов функции abort(). Эта функция посылает процессу сигнал SIGABRT, для которого действие по умолчанию (signal disposition) — завершить процесс и записать образ памяти процесса в core dump файл. Одна из потенциальных угроз при неадекватной настройке окружения — это отказ в обслуживании из-за потребления этими файлами избыточного дискового пространства.
abort()
SIGABRT
Убедиться, что программа была завершена в результате возникновения ошибочной ситуации, можно по коду выхода программы: по умолчанию для программ, собранных SAFEC, это 255.
Опции компилятора, включающие проверки времени выполнения, которые могут спровоцировать аварийное завершение:
-D_FORTIFY_SOURCE=2
-Safe
-fstack-protector
-Safe1
Включить отладочный вывод можно с помощью специальных опций компиляции.
Установка значения макроса _FORTIFY_ABORT с помощью -D_FORTIFY_ABORT поменяет поведение на вызов abort().
_FORTIFY_ABORT
-D_FORTIFY_ABORT
-fno-stack-protector-exit-on-error
__stack_chk_fail()
-fstack-protector-abort-on-error
Способ первый:
-fno-sanitize-undefined-exit-on-error
UBSAN_OPTIONS
UBSAN_OPTIONS=halt_on_error=1,print_stacktrace=true,symbolize=true
Способ второй: изменить поведение на вызов abort() с помощью -fsanitize-undefined-abort-on-error.
-fsanitize-undefined-abort-on-error
Поскольку ядра ОС работают в окружении, отличном от userspace программ, для них применяются специальные режимы компиляции.
В этих случаях некоторые из функций безопасности, включаемых безопасным компилятором для userspace программ, не будут гарантированы. Кроме этого, некоторые сценарии аварийного завершения, применяемые безопасным компилятором по умолчанию, в контексте ядра приводят к нежелательным результатам.
Чтобы избежать снижения уровня безопасности и не спровоцировать проблемы времени выполнения, требуется адаптировать конфигурацию сборки.
-Safe3
KCFLAGS += -O2 -Safe3 -fno-stack-protector-exit-on-error
CONFIG_FORTIFY_SOURCE=y
-nostdinc
-Safe2
Аналогично -Safe3. Заменить -Safe3 на -Safe2.
На данный момент не поддерживается. SAFEC не позволяет достаточно тонко настраивать поведение при срабатывании санитайзеров (WIP), в результате чего в контексте ядра оно безальтернативно приводит к kernel panic. В качестве замены рекомендуем подключать санитайзеры с помощью конфигурационного параметра CONFIG_UBSAN, как описано в linux/Documentation/dev-tools/ubsan.rst.
CONFIG_UBSAN
linux/Documentation/dev-tools/ubsan.rst
Native сборки компилятора для x86_64 скомпонованы относительно Glibc 2.27. Поэтому версия системной Glibc должна быть не ниже этой версии.
Проверить версию можно с помощью ldd --version.
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.
/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 не будет.
safec10
releases/gcc-10
Для версий SAFEC, основанных на активных ветках GCC, указывается релиз, взятый за основу. Например, safe-11.4. После выхода релиза GCC 11.5 мы выпустим версию safec11 на его основе, которая заменит safe-11.4.
safe-11.4
safec11