我怎么应该在铿锵声中使用消毒杀菌剂?

use*_*710 15 c++ llvm clang sanitizer

我很抱歉,如果这是一个超级简单的概念,但我发现很难获得正确的心态,以便正确使用由提供的消毒剂clang.

float foo(float f) { return (f / 0); }
Run Code Online (Sandbox Code Playgroud)

我编译这个小片段

clang++ -fsanitize=float-divide-by-zero -std=c++11 -stdlib=libc++ -c source.cpp -o osan
Run Code Online (Sandbox Code Playgroud)

而且我还在不使用消毒剂的情况下编译了我的对象的"正常"版本

clang++ -std=c++11 -stdlib=libc++ -c source.cpp -o onorm
Run Code Online (Sandbox Code Playgroud)

我期待一些详细的输出,或者来自控制台的一些错误,但是在检查文件时nm我只发现了1个差异

nm o* --demangle

onorm:
0000000000000000 T foo(float)

osan:
                 U __ubsan_handle_divrem_overflow
0000000000000000 T foo(float)
Run Code Online (Sandbox Code Playgroud)

所以在清理版本中有一个未定义的符号,其名称类似于我在编译时使用的清洁剂; 但是一切都是"沉默的",而且从铿锵的前端根本没有输出.

我应该如何使用消毒剂以及什么是正确的工作流程?那个未定义的符号有什么意义?

bam*_*s53 12

未定义的符号是实现清洁剂检查的功能.如果你看一下生成的代码:

没有消毒剂:

_Z3foof:                                # @_Z3foof
    .cfi_startproc
# BB#0:
    xorps   %xmm1, %xmm1
    divss   %xmm1, %xmm0
    ret
Run Code Online (Sandbox Code Playgroud)

使用消毒剂:

_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
Run Code Online (Sandbox Code Playgroud)

您看到它添加了使用该功能进行检查的代码.

编译器应自动链接到相应的清理程序库中,然后为我完成以下完整程序:

float foo(float f) { return (f / 0); }
int main() {
    foo(1.0f);
}
Run Code Online (Sandbox Code Playgroud)

执行时生成以下输出:

main.cpp:1:32: runtime error: division by zero
Run Code Online (Sandbox Code Playgroud)

我使用命令构建并运行 clang++ -fsanitize=undefined main.cpp && ./a.out


如果要进行编译时检查,则需要启用更多编译器警告或静态分析器.但是,似乎没有任何警告或静态分析检查浮点被零除错误.

这是一个生成分析报告的程序:

#include <malloc.h>

int main() {
    int *i = (int*) malloc(sizeof(int));
}
Run Code Online (Sandbox Code Playgroud)

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'
}
^
Run Code Online (Sandbox Code Playgroud)

也可以使用-Weverything [-Wunused-value]检测死存储,但只能通过分析器检测泄漏.

默认情况下,完整分析结果将写入plist文件.您还可以使用以下命令运行分析器:

clang++ --analyze -Xanalyzer -analyzer-output=text main.cpp
clang++ --analyze -Xanalyzer -analyzer-output=html -o html-dir main.cpp
Run Code Online (Sandbox Code Playgroud)

要分别在标准输出或通过带注释的源代码的html显示中详细了解检测到的问题,而不是在plist中.

此处列出分析器检查.

请注意,为了最好地工作,分析器需要分析整个程序,这意味着它需要绑定到构建系统.通常的界面是通过IDE(Xcode)或scan-build带有make 的工具.CMake有一些铿锵的功能,比如生成clang JSON编译数据库文件,但如果CMake对clang分析器有任何内置支持,我不确定.


Sha*_*our 5

因此,如果我们查看控制代码生成中的文档,它会说(强调我的):

打开运行时检查各种形式的未定义或可疑行为.

此选项控制Clang是否为各种形式的未定义或可疑行为添加运行时检查,并且默认情况下处于禁用状态.如果检查失败,则会在运行时生成诊断消息,说明问题.

所以这些是运行时检查而不是编译时间检查.因此,如果您foo在代码中使用,那么您将看到以下输出:

运行时错误:除以零

看到这个例子居住使用-fsanitize=undefined:

float foo(float f) { return (f / 0); }

int main()
{
    int x = 1 << 100 ;
    foo( 2.0f ) ;
}
Run Code Online (Sandbox Code Playgroud)

它会生成两个运行时消息:

main.cpp:6:19:运行时错误:移位指数100对于32位类型'int'来说太大了

main.cpp:2:36:运行时错误:除以零

更新

关于静态检查器,我在回答A C++实现时检测到未定义的行为?我提到了几个工具:STACK,kcc,当然还有Frama-C.

显然clang允许你使用--analyze来运行它的静态检查器,但似乎它最终可能被禁用,运行它的正确方法是通过scan-build.

同样在我自我回答的问题中为什么常量表达式排除了未定义的行为?我展示了如何constexprs在编译时捕获未定义的行为.