Я извиняюсь, если это очень простая концепция, но мне трудно приобрести правильный настрой, чтобы правильно использовать дезинфицирующее средство, предоставляемое clang
,
float foo(float f) { return (f / 0); }
Я собираю этот небольшой фрагмент с
clang++ -fsanitize=float-divide-by-zero -std=c++11 -stdlib=libc++ -c source.cpp -o osan
и я также компилирую «нормальную» версию моего объекта без использования дезинфицирующего средства
clang++ -std=c++11 -stdlib=libc++ -c source.cpp -o onorm
Я ожидал какой-то подробный вывод или некоторую ошибку из консоли, но при проверке файла с nm
Я нашел только 1 разницу
nm o* --demangle
onorm:
0000000000000000 T foo(float)
osan:
U __ubsan_handle_divrem_overflow
0000000000000000 T foo(float)
Таким образом, в очищенной версии есть неопределенный символ с именем, которое напоминает дезинфицирующее средство, которое я использовал при компиляции; но все действительно «тихо» без вывода из внешнего интерфейса clang.
Как я должен использовать дезинфицирующее средство и каков правильный рабочий процесс? Какой смысл в этом неопределенном символе?
Неопределенный символ — это функция, которая реализует проверку дезинфицирующего средства. Если вы посмотрите на сгенерированный код:
Без дезинфицирующего средства:
_Z3foof: # @_Z3foof
.cfi_startproc
# BB#0:
xorps %xmm1, %xmm1
divss %xmm1, %xmm0
ret
С дезинфицирующим средством:
_Z3foof: # @_Z3foof
.cfi_startproc
.long 1413876459 # 0x54460aeb
.quad _ZTIFffE
# BB#0:
pushq %rax
.Ltmp1:
.cfi_def_cfa_offset 16
movss %xmm0, 4(%rsp) # 4-byte Spill
movd %xmm0, %esi
movl $__unnamed_1, %edi
xorl %edx, %edx
callq __ubsan_handle_divrem_overflow
xorps %xmm1, %xmm1
movss 4(%rsp), %xmm0 # 4-byte Reload
divss %xmm1, %xmm0
popq %rax
ret
Вы видите, что добавлен код для проверки с использованием этой функции.
Компилятор должен автоматически связать соответствующую библиотеку с дезинфицирующим средством и затем для меня следующую полную программу:
float foo(float f) { return (f / 0); }
int main() {
foo(1.0f);
}
При выполнении выдает следующий вывод:
main.cpp:1:32: runtime error: division by zero
Я построил и побежал с помощью команды clang++ -fsanitize=undefined main.cpp && ./a.out
Если вам нужны проверки во время компиляции, вы должны включить больше предупреждений компилятора или статический анализатор. Однако, похоже, что нет никаких предупреждений или статических проверок для ошибок деления на ноль с плавающей запятой.
Вот программа, которая выдает отчет анализатора:
#include <malloc.h>
int main() {
int *i = (int*) malloc(sizeof(int));
}
Составлено с clang++ -std=c++11 main.cpp
он не производит диагностику, но скомпилирован с clang++ -std=c++11 --analyze main.cpp
он сообщает следующее:
main.cpp:4:10: warning: Value stored to 'i' during its initialization is never read
int *i = (int*) malloc(sizeof(int));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:5:1: warning: Potential leak of memory pointed to by 'i'
}
^
Мертвое хранилище также можно обнаружить с помощью -Weverything [-Wunused-value], но утечка обнаруживается только анализатором.
По умолчанию полные результаты анализа записываются в файл plist. Вы также можете запустить анализатор с помощью команд:
clang++ --analyze -Xanalyzer -analyzer-output=text main.cpp
clang++ --analyze -Xanalyzer -analyzer-output=html -o html-dir main.cpp
Чтобы получить подробные пошаговые инструкции по обнаруженным проблемам в стандартном выводе или через html-отображение аннотированного исходного кода соответственно, а не в plist.
Проверки анализатора перечислены Вот.
Обратите внимание, что для лучшей работы анализатор должен анализировать целые программы, что означает, что он должен быть связан с системой сборки. Обычный интерфейс через IDE (XCode) или scan-build
инструмент с маркой. CMake имеет некоторые функции лягушки, такие как создание лязга Файлы базы данных компиляции JSON но я не уверен, что CMake имеет встроенную поддержку для анализатора clang.
Так что, если мы посмотрим на документацию в Управление генерацией кода это говорит (акцент мой):
Включи проверки во время выполнения для различных форм неопределенного или подозрительного поведения.
Эта опция контролирует, добавляет ли Clang проверки во время выполнения для различных форм неопределенного или подозрительного поведения, и по умолчанию отключена. Если проверка не удалась, диагностическое сообщение выдается во время выполнения, объясняющее проблему.
так что это проверки времени выполнения, а не проверки времени компиляции. Так что если вы использовали foo
в вашем коде вы увидите следующий вывод:
ошибка времени выполнения: деление на ноль
Смотрите этот пример жить с помощью -fsanitize=undefined
:
float foo(float f) { return (f / 0); }
int main()
{
int x = 1 << 100 ;
foo( 2.0f ) ;
}
он генерирует два сообщения во время выполнения:
main.cpp: 6: 19: ошибка времени выполнения: показатель сдвига 100 слишком велик для 32-битного типа int
main.cpp: 2: 36: ошибка времени выполнения: деление на ноль
Обновить
Что касается статических шашек, в моем ответе Реализация C ++, которая обнаруживает неопределенное поведение? Я упоминаю несколько инструментов: СТЕК, КСС и конечно Frama-С.
По-видимому clang
позволяет использовать —проанализировать, чтобы запустить это статическая проверка но кажется, что он может быть отключен в конце концов, и правильный путь для его запуска будет через скан-сборки.
Также в моем ответе на вопрос Почему константные выражения имеют исключение для неопределенного поведения? Я показываю как constexprs
может использоваться для отлова неопределенного поведения во время компиляции.