How I'm supposed to use the sanitizer in clang?
Asked Answered
H

2

16

I'm sorry if this is a uber-easy concept, but I find hard to acquire the right mindset in order to correctly use the sanitizer provided by clang.

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

I compile this small snippet with

clang++ -fsanitize=float-divide-by-zero -std=c++11 -stdlib=libc++ -c source.cpp -o osan

and I also compile a "normal" version of my object without using the sanitizer

clang++ -std=c++11 -stdlib=libc++ -c source.cpp -o onorm

I was expecting some verbose output, or some error from the console, but when inspecting the file with nm I only found 1 difference

nm o* --demangle

onorm:
0000000000000000 T foo(float)

osan:
                 U __ubsan_handle_divrem_overflow
0000000000000000 T foo(float)

So in the sanitized version there is an undefined symbol with a name that resembles the sanitizer that I was using when compiling this; but everything is really "silent" with no output at all from the clang frontend .

How I'm supposed to use the sanitizer and what is the right workflow ? What's the point of that undefined symbol ?

Halpern answered 27/3, 2014 at 21:46 Comment(6)
You should link your program into executable and run it. Sanitizers are runtime tools.Sagamore
@Sagamore funny thing is, in the docs that I have found no one ever compiles and runs something with a main function in it ... so I was getting the idea that this are compile time tools .Halpern
user2485710, which docs? Clang's manual says clang.llvm.org/docs/UsersManual.html "Controlling Code Generation ... -fsanitize=... Turn on runtime checks for various forms of undefined or suspicious behavior.... If a check fails, a diagnostic message is produced at runtime explaining the problem. "Sagamore
@Sagamore docs from the llvm project, I don't have the links for them, probably it was something distributed along with the source code itself, but really it was kinda confusing and gave me that idea about sanitizer . There should be more emphasis on the "runtime" phase before starting any tutorial on this.Halpern
@Sagamore nope, it wasn't that page for sure.Halpern
I updated my answer with some interesting details.Aerostat
H
13

The undefined symbol is a function that implements the sanitizer's check. If you look at generated code:

No sanitizer:

_Z3foof:                                # @_Z3foof
    .cfi_startproc
# BB#0:
    xorps   %xmm1, %xmm1
    divss   %xmm1, %xmm0
    ret

With sanitizer:

_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

You see it's added the code to do the check using that function.

The compiler should automatically link in the appropriate sanitizer library and then for me the following complete program:

float foo(float f) { return (f / 0); }
int main() {
    foo(1.0f);
}

Produces the following output when executed:

main.cpp:1:32: runtime error: division by zero

I built and ran using the command clang++ -fsanitize=undefined main.cpp && ./a.out


If you want compile-time checks you want to either enable more compiler warnings or the static analyzer. However there doesn't seem to be any warning or static analysis check for floating point divide-by-zero errors.

Here's a program that produces an analyzer report:

#include <malloc.h>

int main() {
    int *i = (int*) malloc(sizeof(int));
}

Compiled with clang++ -std=c++11 main.cpp it produces no diagnostics, but compiled with clang++ -std=c++11 --analyze main.cpp it reports the following:

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'
}
^

The dead store can also be detected with -Weverything [-Wunused-value], but the leak is only detected by the analyzer.

By default full analysis results are written to a plist file. You can also run the analyzer with the commands:

clang++ --analyze -Xanalyzer -analyzer-output=text main.cpp
clang++ --analyze -Xanalyzer -analyzer-output=html -o html-dir main.cpp

To get detailed walk-throughs of detected issues on the standard output or via html display of annotated source code respectively, instead of in a plist.

Analyzer checks are listed here.

Note that to work best the analyzer needs to analyze whole programs, which means it needs to tie into the build system. The usual interface is via an IDE (Xcode) or the scan-build tool with make. CMake has some clang features such as producing clang JSON compilation database files but I'm not sure off hand if CMake has any built in support for the clang analyzer.

Homolographic answered 27/3, 2014 at 21:56 Comment(6)
what compiler options did you use in order to compile this ?Halpern
@Halpern clang++ -fsanitize=undefined main.cpp && ./a.outHomolographic
and you get no dynamically linked libraries ? this works for me but it looks like the llvm libraries involved in this are statically linked.Halpern
@Halpern Yeah I think it gets statically linked, though I know llvm builds dynamic libs for the sanitizers as well. You can give clang the -v option to see the exact commands and parameters the driver uses under the covers to do the compilation/linking.Homolographic
thanks for the nice addition, static analysis works as expected too .Halpern
I found an interesting gist that discusses the --analyze flag it seems like it may go away in the long run.Aerostat
A
5

So if we look at the documentation in the the Controlling Code Generation it says (emphasis mine):

Turn on runtime checks for various forms of undefined or suspicious behavior.

This option controls whether Clang adds runtime checks for various forms of undefined or suspicious behavior, and is disabled by default. If a check fails, a diagnostic message is produced at runtime explaining the problem.

so these are runtime checks not compile time checks. So if you used foo in your code then you would see the following output:

runtime error: division by zero

See this example live using -fsanitize=undefined:

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

int main()
{
    int x = 1 << 100 ;
    foo( 2.0f ) ;
}

it generates two run-time messages:

main.cpp:6:19: runtime error: shift exponent 100 is too large for 32-bit type 'int'

main.cpp:2:36: runtime error: division by zero

Update

With respect to static checkers, in my answer to A C++ implementation that detects undefined behavior? I mention several tools: STACK, kcc and of course Frama-C.

Apparently clang allows you to use --analyze to run it's static checker but it seems like it may be disabled eventually and the the correct way to run it would be through scan-build.

Also in my self-answered question Why do constant expressions have an exclusion for undefined behavior? I show how constexprs can be used to catch undefined behavior at compile time.

Aerostat answered 27/3, 2014 at 21:56 Comment(8)
Ah, yeah. If he was expecting compile time checks the sanitizers are the wrong tool. The static analyzer does more compile-time checks.Homolographic
now I understand this, but I still think that it could be more useful with more verbose output. What is the static analyzer of reference for llvm/clang ?Halpern
@Halpern my answer to A C++ implementation that detects undefined behavior? has some good references to static checkers.Aerostat
so the clang project offers sanitizers but doesn't offer a static analyzer ? I have to refer to other projects for this ?Halpern
I will answer my own question: apparently the clang-analyzer.llvm.org/scan-build.html page it's not mentioning the fact that the tool is already being distributed with clang sources ...Halpern
@Halpern If you want to run the static analyzer on a single file the flag is --analyze and it outputs a plist file with the results. I think there's a way to make it output html, but I can't recall it at the moment. The analyzer doesn't report anything for floating point divide-by-zero errors.Homolographic
@Homolographic thanks for that hint, what kind of errors is able to spot than ? I would like to test it with something that is supposed to trigger an error.Halpern
@Halpern I've added an example to my answer.Homolographic

© 2022 - 2024 — McMap. All rights reserved.