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 ?
MemorySanitizer (MSan) is a detector of uninitialized memory reads. This Sanitizer finds the cases when stack- or heap-allocated memory is read before it is written. MSan is also capable of tracking uninitialized bits in a bitfield.
AddressSanitizer (or ASan) is an open source programming tool that detects memory corruption bugs such as buffer overflows or accesses to a dangling pointer (use-after-free).
AddressSanitizer is a fast memory error detector. It consists of a compiler instrumentation module and a run-time library. The tool can detect the following types of bugs: Out-of-bounds accesses to heap, stack and globals. Use-after-free.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With