Tuesday, March 25, 2014

ASAN and libraries (2nd part)

In ASAN and libraries I claimed you didn't need to compile a library with -fsanitize=address to get ASAN to work over it. Well it turns out that is only true in some cases and in some others like the one in this example you actually need it. So here comes a different example and the differences of using -fsanitize=address and not in the library code.

shared.cpp
#include "shared.h"

Foo::Foo()
{
int a[1];
a[2] = 3;
}



shared.h
class Foo
{
public:
Foo();
};



main.cpp
#include "shared.h"

int main(int, char **)
{
Foo f;
return 0;
}

Let's see what happens if we do
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4
export ASAN_OPTIONS=symbolize=1
g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3
g++ -fno-omit-frame-pointer -fsanitize=address main.cpp -lshared -L . -g3
LD_LIBRARY_PATH=. ./a.out
As the suggested in the previous blog entry.

Nothing, no error detected.

But if we change to
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4
export ASAN_OPTIONS=symbolize=1
g++ -fno-omit-frame-pointer -fsanitize=address -Wl,--no-undefined \
    -shared -o libshared.so shared.cpp -g3 -lasan -fPIC
g++ -fno-omit-frame-pointer -fsanitize=address main.cpp -lshared -L . -g3
LD_LIBRARY_PATH=. ./a.out

==13069== ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffe74fafa8
at pc 0x7fb0ef71f792 bp 0x7fffe74faf60 sp 0x7fffe74faf58
WRITE of size 4 at 0x7fffe74fafa8 thread T0
    #0 0x7fb0ef71f791 in Foo::Foo() /home/tsdgeos/test/shared.cpp:6
    #1 0x40074a in main /home/tsdgeos/test/main.cpp:5
    #2 0x7fb0ef37aec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
    #3 0x400638 in _start (/home/tsdgeos/test/a.out+0x400638)
Address 0x7fffe74fafa8 is located at offset 40 in frame <__base_ctor> of T0's stack:

Boom! We get the error :-) So my conclusion that ASAN wasn't needed on libraries was bad. You need it. Thanks Zecke for pointing me to my wrongness :-)

Saturday, March 22, 2014

ASAN and plugins

In ASAN and libraries Milian asked if the reasoning for libraries also applied for plugins. Since I had no idea, I had to try it.

Here comes the output

main.cpp
#include <QDebug>
#include <QLibrary>

int main(int, char **)
{
    QLibrary l("libshared");
    qDebug() << l.load();

    return 0;
}
shared.cpp
#include "shared.h"

static Foo f;

Foo::Foo()
{
    int *a = 0;
    *a = 33;
}
shared.h
class Foo
{
public:
    Foo();
};
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4
export ASAN_OPTIONS=symbolize=1
g++ -shared -o libshared.so shared.cpp  -g3 -fPIC
g++ -fsanitize=address main.cpp -g3 -I /usr/include/qt4/QtCore/ \
    -I /usr/include/qt4/ -lQtCore
And then we run it!
$ LD_LIBRARY_PATH=. ./a.out 
ASAN:SIGSEGV
=================================================================
==7048== ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
(pc 0x7f199c1326aa sp 0x7fff37e557c0 bp 0x7fff37e557c0 T0)
AddressSanitizer can not provide additional info.
 #0 0x7f199c1326a9 in Foo::Foo() /home/tsdgeos/test/shared.cpp:8
 #1 0x7f199c1326da in __static_initialization_and_destruction_0(int, int) 
    /home/tsdgeos/test/shared.cpp:3
 #2 0x7f199c1326ef in _GLOBAL__sub_I_shared.cpp /home/tsdgeos/test/shared.cpp:9
 #3 0x7f19a132b139 (/lib64/ld-linux-x86-64.so.2+0x10139)
 #4 0x7f19a132b222 (/lib64/ld-linux-x86-64.so.2+0x10222)
 #5 0x7f19a132fc6f (/lib64/ld-linux-x86-64.so.2+0x14c6f)
 #6 0x7f19a132aff3 (/lib64/ld-linux-x86-64.so.2+0xfff3)
 #7 0x7f19a132f3ba (/lib64/ld-linux-x86-64.so.2+0x143ba)
 #8 0x7f199d1a602a (/lib/x86_64-linux-gnu/libdl.so.2+0x102a)
 #9 0x7f19a132aff3 (/lib64/ld-linux-x86-64.so.2+0xfff3)
 #10 0x7f199d1a662c (/lib/x86_64-linux-gnu/libdl.so.2+0x162c)
 #11 0x7f199d1a60c0 (/lib/x86_64-linux-gnu/libdl.so.2+0x10c0)
 #12 0x7f199e0156b7 (/usr/lib/x86_64-linux-gnu/libQtCore.so.4+0x16e6b7)
 #13 0x7f199e010599 (/usr/lib/x86_64-linux-gnu/libQtCore.so.4+0x169599)
 #14 0x4011c0 in main /home/tsdgeos/test/main.cpp:8
 #15 0x7f199d5e8ec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
 #16 0x401078 in _start (/home/tsdgeos/test/a.out+0x401078)
SUMMARY: AddressSanitizer: SEGV /home/tsdgeos/test/shared.cpp:8 Foo::Foo()
==7048== ABORTING

So it seems that "plugins are just libraries" applies here :)

Thursday, March 20, 2014

ASAN and libraries

Yesterday we saw how to get line numbers in the stacktrace when using ASAN and gcc, today we're going to see how to deal with ASAN and libraries. For that we're going to use these three very simple files

shared.cpp
#include "shared.h"

Foo::Foo()
{
    int *a = 0;
    *a = 33;
}


shared.h
class Foo
{
public:
    Foo();
};


main.cpp
#include "shared.h"

int main(int, char **)
{
    Foo f;
    return 0;
}

We do the initial compilation:
g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3
g++ main.cpp -lshared -L . -g3
LD_LIBRARY_PATH=. ./a.out
and it segfaults :)

Now we want to use ASAN to find out what's wrong, where do we add the -fsanitize=address -fno-omit-frame-pointer? Logic would say that we add it to both places, but actually it's only necessary to add it to the final binary, i.e

g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3
g++ -fno-omit-frame-pointer -fsanitize=address main.cpp -lshared -L . -g3
LD_LIBRARY_PATH=. ./a.out
and we get
==10226== ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
(pc 0x7f497563f66a sp 0x7fff77cc4310 bp 0x7fff77cc4310 T0)
AddressSanitizer can not provide additional info.
    #0 0x7f497563f669 in Foo::Foo() /home/tsdgeos/test/shared.cpp:6
    #1 0x40074a in main /home/tsdgeos/test/main.cpp:5
    #2 0x7f497529aec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
    #3 0x400638 in _start (/home/tsdgeos/test/a.out+0x400638)
SUMMARY: AddressSanitizer: SEGV /home/tsdgeos/test/shared.cpp:6 Foo::Foo()

There you go, no need to add the -fsanitize=address flag to the library compilation :)

Now, until a few minutes ago I did not know this, I actually was adding -fsanitize=address to the library itself; that comes with a few problems that i'll explain how i solved (just for completeness, since the above example shows you don't need it)

My logic was that by looking at main.cpp i knew nothing could be wrong there so i decided i wanted sanitize the library, my first attempt was:

g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3 \
-fno-omit-frame-pointer -fsanitize=address
which gives a linking error
/tmp/cc18Wen3.o: In function `Foo::Foo()':
/home/tsdgeos/test/shared.cpp:6: undefined reference to `__asan_report_store4'
/usr/bin/ld: /tmp/cc18Wen3.o: relocation R_X86_64_PC32 against undefined symbol
`__asan_report_store4' can not be used when making a shared object;
recompile with -fPIC
/usr/bin/ld: final link failed: Bad value

OK, so we are told to recompile with -fPIC, easy peasy

g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3 \
-fno-omit-frame-pointer -fsanitize=address -fPIC
Which still fails to link!
/tmp/ccNkpRZo.o: In function `Foo::Foo()':
/home/tsdgeos/test/shared.cpp:6: undefined reference to `__asan_report_store4'
/tmp/ccNkpRZo.o: In function `_GLOBAL__sub_I_00099_0_shared.cpp':
/home/tsdgeos/test/shared.cpp:7: undefined reference to `__asan_init_v1'
collect2: error: ld returned 1 exit status

OK, so if ASAN symbols are missing, let's just compile libasan in

g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3 \
-fno-omit-frame-pointer -fsanitize=address -fPIC -lasan
Links!

Next since I know nothing is wrong in main.cpp I don't need ASAN in there

g++  main.cpp -lshared -L . -g3
Compiles!

But when run it segfaults :-/ And if you debug it you'll see a stack trace like
#0  0x0000000000000000 in ?? ()
#1  0x00007ffff4894bf1 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#2  0x00007ffff4894e32 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#3  0x00007ffff4895d9b in __asan_init_v1 () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#4  0x00007ffff7bd8776 in _GLOBAL__sub_I_00099_0_shared.cpp(void) () at shared.cpp:7
#5  0x00007ffff7dea13a in call_init (l=, argc=argc@entry=1, argv=argv@entry=0x7fffffffde18, env=env@entry=0x7fffffffde28) at dl-init.c:78
#6  0x00007ffff7dea223 in call_init (env=, argv=, argc=, l=) at dl-init.c:36
#7  _dl_init (main_map=0x7ffff7ffe1c8, argc=1, argv=0x7fffffffde18, env=0x7fffffffde28) at dl-init.c:126
#8  0x00007ffff7ddb30a in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
#9  0x0000000000000001 in ?? ()
#10 0x00007fffffffe1ca in ?? ()
#11 0x0000000000000000 in ?? ()

So something weird in ASAN thing is going on, let's compile -lasan in also
g++  main.cpp -lshared -L . -g3 -lasan

Same crash :-/

Ok, so then you think, well, let's just add the fsanitize in. And then it works, but as demonstrated a few lines ago, the whole thing of adding -fsanitize=address and -lasan to the library was unneeded since all that was required was just adding -fsanitize=address to the binary when it's compiled and that's it :)

Wednesday, March 19, 2014

ASAN and gcc: How to get line numbers in the stacktrace

ASAN or Address Sanitizer is a nice project by Google that is a memory error detector for C/C++. According to their web it finds:
  • Use after free (dangling pointer dereference)
  • buffer overflow
  • Stack buffer overflow
  • Global buffer overflow
  • Use after return
  • Initialization order bugs

Also it's much faster than valgrind :-) Unfortunately it's not as easy to use as valgrind since it requires a rebuild of your code.

The basic instructions to use ASAN are:
Use the -fsanitize=address switch
together with the suggestion of
To get nicer stack traces in error messages add -fno-omit-frame-pointer.

As a simple example let's see what happens when we run
int main(int, char **)
{
    int a[3];
    a[3] = 4;
    return 0;
}
compiled with
g++ -fno-omit-frame-pointer -fsanitize=address main.cpp

The result:
==8403== ERROR: AddressSanitizer: stack-buffer-overflow
on address 0x7fffbe7c7d8c at pc 0x400779
bp 0x7fffbe7c7d40 sp 0x7fffbe7c7d38
WRITE of size 4 at 0x7fffbe7c7d8c thread T0
    #0 0x400778 (/home/tsdgeos_work/test/a.out+0x400778)
    #1 0x7f2f5cfa6ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
    #2 0x400638 (/home/tsdgeos_work/test/a.out+0x400638)
Address 0x7fffbe7c7d8c is located at offset 44 in frame 
of T0's stack: This frame has 1 object(s): [32, 44) 'a'

In this case it's pretty obvious what's wrong with just looking at the code and ASAN is even telling us it is a bad write just after the 'a' object, but note that we are not getting the line number of where the error happens. You'll say, that's why you didn't use the -g switch! So let's see what do we get after compiling with
g++ -g3 -fno-omit-frame-pointer -fsanitize=address main.cpp

The result:
==8467== ERROR: AddressSanitizer: stack-buffer-overflow
on address 0x7fff59ccaa2c at pc 0x400779
bp 0x7fff59cca9e0 sp 0x7fff59cca9d8
WRITE of size 4 at 0x7fff59ccaa2c thread T0
    #0 0x400778 (/home/tsdgeos_work/test/a.out+0x400778)
    #1 0x7f6f75601ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
    #2 0x400638 (/home/tsdgeos_work/test/a.out+0x400638)
Address 0x7fff59ccaa2c is located at offset 44 in frame 
of T0's stack: This frame has 1 object(s): [32, 44) 'a'

Same thing :-/

Here is where people may give up or think to switch to clang and try there, but since clang is sometimes more picky with the code (not bad per se) it may be a lot of work to get your code to compile under clang, if you keep digging you'll read some news about GCC not having a ASAN symbolizer [yet], but clang has one and you can still use it with GCC, so if you
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4
export ASAN_OPTIONS=symbolize=1
and then run the binary again (the one we just compiled with -g3)

The result:
WRITE of size 4 at 0x7fff4f5f0ebc thread T0
    #0 0x400778 in main /home/tsdgeos_work/test/main.cpp:5
    #1 0x7f3d4dce2ec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
    #2 0x400638 in _start (/home/tsdgeos_work/test/a.out+0x400638)
Address 0x7fff4f5f0ebc is located at offset 44 in frame 
of T0's stack: This frame has 1 object(s): [32, 44) 'a'

And now we have line numbers :-)

Akademy-es 2014 in Málaga

This year, the 9th Akademy-es will take place in Málaga from 16 to 18 May.

Málaga is the city that hosted Akademy 2005 and were a few few crazy people met and thought about doing Akademy-es 2006 next year in Barcelona, and after that, 9 years in a row. Not bad :-)

I'm definitely going and you should if you're interested in KDE too. And of course you should think about submitting a talk :-)
Finally if you have a company you may want to join and sponsor the event, have a look at the sponsor page.

Registration is still not open but will be in a few days/weeks.

Hope to see you there :-)