Using gcc’s 4.8.0 Address Sanitizer with Qt

Published Wednesday April 17th, 2013 | by

One of the cool new features of gcc 4.8 is the built in “Address Sanitizer”: a memory error detector for C/C++ that will tell you instantly when you e.g. access already deleted memory. This is actually a Google project from Clang/LLVM, so for LLVM users this might be old stuff, but it wasn’t for me :) Since documentation on every day use is still a bit scarce on the web, I’m dumping the gist of how to put it to good use here, especially in the Qt context …

How does it work?

It basically overwrites malloc and free, and does check the memory before every access (see the project wiki for the details). Apparently it does that in a very efficient manner, since the slow down is only about 2x compared to uninstrumented execution! Who knows, maybe we can enable it for the Qt-Project CI system at one point?

Be warned though that it only works so far on Linux and Mac. No luck for MinGW :(

How to enable it?

Since it is part of the compiler suite enabling it is easy: just add -fsanitize=address -fno-omit-frame-pointer to the compiler calls, and -fsanitize=address to the linker calls. Anyhow, to catch issues where the memory is allocated, de-allocated or accessed by Qt you do not only have to instrument your application, but also Qt. There’s a tentative patch for Qt 5.2 which makes this easy:

https://codereview.qt-project.org/#change,43420

It is scheduled for the dev branch (Qt 5.2) because it’s a new feature, but you should be fine cherry-picking it to e.g. Qt 5.0. You can then configure Qt with -address-sanitizer, and run qmake CONFIG+=address_sanitizer for your own applications.

If you don’t want to cherry-pick, you can also pass the additional command line arguments to qmake by explicitly setting QMAKE_CXXFLAGS, QMAKE_CFLAGS, and QMAKE_LFLAGS manually:

$ qmake QMAKE_CXXFLAGS+="-fsanitize=address -fno-omit-frame-pointer" \
QMAKE_CFLAGS+="-fsanitize=address -fno-omit-frame-pointer" \
QMAKE_LFLAGS+="-fsanitize=address"

How to use it?

Just run your application! If you happen to hit a memory issue it will abort, and show you a stack trace with module names and addresses. You will need a separate tool called asan_symbolize.py to get the symbols, and then maybe c++filt to de-mangle the C++ symbols.

Demo!

$ mkdir addresssanitizertest
$ echo '
#include <QDebug>
int main(int, char *[]) {
const char *str = QString("Evil!").toLocal8Bit().constData();
qDebug() << str;
}
' > addresssanitizertest/main.cpp
$ cd addresssanitizertest && qmake -project && qmake CONFIG+=address_sanitizer

$ ./addresssanitizertest 2>&1 | asan_symbolize.py | c++filt
=================================================================
==32195== ERROR: AddressSanitizer: heap-use-after-free on address 0x600c0000bcd8 at pc 0x4016ce bp 0x7fff7ccd86c0 sp 0x7fff7ccd86b8
READ of size 1 at 0x600c0000bcd8 thread T0
    #0 0x4016cd in QString::fromUtf8(char const*, int) /home/kkoehne/dev/qt/qt-5.1-gcc-4.8.0-64/qtbase/include/QtCore/../../../../qt-5.1/qtbase/src/corelib/tools/qstring.h:478
    #1 0x401b1e in QDebug::operator<<(char const*) /home/kkoehne/dev/qt/qt-5.1-gcc-4.8.0-64/qtbase/include/QtCore/../../../../qt-5.1/qtbase/src/corelib/io/qdebug.h:117
    #2 0x401282 in main /tmp/addresssanitizertest/main.cpp:6 (discriminator 1)
    #3 0x7fac5c1e3a14 in __libc_start_main ??:?
    #4 0x401118 in _start /home/abuild/rpmbuild/BUILD/glibc-2.17/csu/../sysdeps/x86_64/start.S:123
0x600c0000bcd8 is located 24 bytes inside of 64-byte region [0x600c0000bcc0,0x600c0000bd00)
freed by thread T0 here:
    #0 0x7fac5eab9c5a in __interceptor_free _asan_rtl_
    #1 0x7fac5d353e59 in QArrayData::deallocate(QArrayData*, unsigned long, unsigned long) /home/kkoehne/dev/qt/qt-5.1/qtbase/src/corelib/tools/qarraydata.cpp:125 (discriminator 2)
    #2 0x401c21 in QTypedArrayData::deallocate(QArrayData*) /home/kkoehne/dev/qt/qt-5.1-gcc-4.8.0-64/qtbase/include/QtCore/../../../../qt-5.1/qtbase/src/corelib/tools/qarraydata.h:230
    #3 0x401630 in QByteArray::~QByteArray() /home/kkoehne/dev/qt/qt-5.1-gcc-4.8.0-64/qtbase/include/QtCore/../../../../qt-5.1/qtbase/src/corelib/tools/qbytearray.h:396 (discriminator 1)
    #4 0x401231 in main /tmp/addresssanitizertest/main.cpp:5 (discriminator 1)
    #5 0x7fac5c1e3a14 in __libc_start_main ??:?
previously allocated by thread T0 here:
    #0 0x7fac5eab9e7f in __interceptor_realloc _asan_rtl_
    #1 0x7fac5d35944e in QByteArray::reallocData(unsigned int, QFlags) /home/kkoehne/dev/qt/qt-5.1/qtbase/src/corelib/tools/qbytearray.cpp:1472
    #2 0x7fac5d358d05 in QByteArray::resize(int) /home/kkoehne/dev/qt/qt-5.1/qtbase/src/corelib/tools/qbytearray.cpp:1431
    #3 0x7fac5d77e452 in QUtf8::convertFromUnicode(QChar const*, int, QTextCodec::ConverterState*) /home/kkoehne/dev/qt/qt-5.1/qtbase/src/corelib/codecs/qutfcodec.cpp:130
    #4 0x7fac5d780e91 in QUtf8Codec::convertFromUnicode(QChar const*, int, QTextCodec::ConverterState*) const /home/kkoehne/dev/qt/qt-5.1/qtbase/src/corelib/codecs/qutfcodec.cpp:507
    #5 0x7fac5d77a483 in QTextCodec::fromUnicode(QString const&) const /home/kkoehne/dev/qt/qt-5.1/qtbase/src/corelib/codecs/qtextcodec.cpp:807
    #6 0x7fac5d48dd26 in QString::toLocal8Bit() const /home/kkoehne/dev/qt/qt-5.1/qtbase/src/corelib/tools/qstring.cpp:4020
    #7 0x401215 in main /tmp/addresssanitizertest/main.cpp:5
    #8 0x7fac5c1e3a14 in __libc_start_main ??:?
Shadow bytes around the buggy address:
  0x0c01ffff9740: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c01ffff9750: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c01ffff9760: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c01ffff9770: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c01ffff9780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c01ffff9790: fa fa fa fa fa fa fa fa fd fd fd[fd]fd fd fd fd
  0x0c01ffff97a0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
  0x0c01ffff97b0: 00 00 00 00 00 00 00 fa fa fa fa fa 00 00 00 00
  0x0c01ffff97c0: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 fa
  0x0c01ffff97d0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
  0x0c01ffff97e0: 00 00 00 00 00 00 01 fa fa fa fa fa 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==32195== ABORTING

Have fun hunting down memory issues :)

Update: Apparently a stock gcc 4.8.0 has still issues with address-sanitizer: libasan isn’t linked automatically, an internal compiler error in qtbase … I’m personally using a gcc from 4.8 branch.

Did you like this? Share it:

Posted in Build system, C++, Compilers, Debugging, Qt | Tags: , , , ,

19 comments to Using gcc’s 4.8.0 Address Sanitizer with Qt

Bruno says:

What the the advantages over valgrind ?

Kai Koehne says:

It’s by a factor faster :) Also I tend to get lots of false positives for valgrind/memcheck, while every issue AddressSanitizer has shown to me so far was a real issue.

Some more details are also at http://code.google.com/p/address-sanitizer/wiki/ComparisonOfMemoryTools

Eugene says:

Interesting. Have you also tried using ThreadSanitizer (a data race detector) this way? GCC 4.8 seems to support it too.

Kai Koehne says:

I tried to just enable it along the lines, but had issues then with e.g. linking moc (IIRC symbols missing when compiled with -fPIE, -pie …). Gave up on it for the moment.

Eugene says:

I see. Thanks for the info.

Jeff Tranter says:

What’s the best way to get gcc 4.8.0 on, say, Ubuntu Linux – build it from source?

Kai Koehne says:

That’s at least what I did :)

svn checkout svn://gcc.gnu.org/svn/gcc/branches/gcc-4_8-branch gcc-4_8
mkdir gcc-4_8-build && cd gcc-4_8-build
../gcc-4_8/configure -prefix /usr/local/gcc-4.8
make && sudo make install

Andrey says:

On Windows there is more powerful feature: page heap which can be enabled by gflags. No need to recompile binaries.

tr3w says:

It’s not “more powerful”, it’s different.
You can see the comparison here (under Guard Page).

http://code.google.com/p/address-sanitizer/wiki/ComparisonOfMemoryTools

Thanks for bringing this to my attention. Looks very useful. :-)

Philip says:

I wasn’t able to get it to work on mac 10.8, Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn).

I get “clang: warning: argument unused during compilation: ‘-fsanitize=address'”

Any ideas?

Philip says:

It works if you download llvm from: http://llvm.org/releases/download.html#svn , instead of using XCode version.

HGH says:

Do we need to self-compile Qt to use MinGW 4.8?

Kai Koehne says:

The current idea is to also package 5.1.0 with a mingw-builds gcc 4.8.0 32 bit toolchain..

Zero says:

How about adding “Address Sanitizer” feature to qbs ?

Joe says:

code like “QString(“Evil!”).toLocal8Bit().constData();” should be detected and warned by QtCreator instantly. They have two or more ‘.’ operator and return a pointer.

Kai Koehne says:

Good idea actually. Can you file a suggestion to qt-creator? https://bugreports.qt-project.org/browse/QTCREATORBUG

Ian Monroe says:

Doesn’t the kernel already do a good job of telling you when you access deleted data? :D I’m sure it’s useful, just the examples all look like SEGFAULTs to me.

Kai Koehne says:

SEGFAULT is an OS mechanism, which might or might not occur when you access invalid memory. Anyhow, allocations / deallocations in C/C++ do not directly map to memory pages, so for Linux, Mac, Windows at least I don’t think that this reliably catches issues like the example above.

Commenting closed.