Что делать со срабатыванием -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?

Установка значения макроса _FORTIFY_ABORT с помощью -D_FORTIFY_ABORT поменяет поведение на вызов abort().

Как отлаживать аварийный останов из-за -fstack-protector?

Как отлаживать аварийный останов из-за срабатывания санитайзеров?

Способ первый:

  1. передать компилятору -fno-sanitize-undefined-exit-on-error для вызова специфичных для конкретных случаев неопределённого поведения встроенных функций и функций библиотеки libubsan (поведение базового GCC); и
  2. выставить значение переменной окружения UBSAN_OPTIONS при запуске программы: UBSAN_OPTIONS=halt_on_error=1,print_stacktrace=true,symbolize=true.

Способ второй: изменить поведение на вызов abort() с помощью -fsanitize-undefined-abort-on-error.

Особенности сборки ядра Linux

Поскольку ядра ОС работают в окружении, отличном от userspace программ, для них применяются специальные режимы компиляции.

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

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

-Safe3

-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 версии системного, по следующим причинам.

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

  2. Если использовать безопасный компилятор более новой версии, чем 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.