Skip to content

Instantly share code, notes, and snippets.

@kishmakov
Last active May 9, 2024 14:35
Show Gist options
  • Save kishmakov/6c186c738210c98bbe886cb861358b18 to your computer and use it in GitHub Desktop.
Save kishmakov/6c186c738210c98bbe886cb861358b18 to your computer and use it in GitHub Desktop.
Shared libraries and Address Sanitizer (ASAN)

Readme

This is an example of ASAN usage for sanitizing of shared library code. The following cases are considered:

  1. Library is compiled without sanitizer, while binary is compiled with sanitizer;
  2. Library and binary are compiled with sanitizer;
  3. Library is compiled with sanitizer, while binary is compiled without sanitizer.

1

In this case sanitizer doesn't find the error.

  • make lib-gcc && make bin-gcc-ldynamic
  • make lib-gcc && make bin-gcc-lstatic (output presented)
  • make lib-gcc && make bin-clang-ldynamic
  • make lib-gcc && make bin-clang-lstatic

2

In this case sanitizer works well.

  • make lib-gcc-asan && make bin-gcc-ldynamic
  • make lib-gcc-asan && make bin-gcc-lstatic (output presented)
  • make lib-clang-asan && make bin-clang-ldynamic
  • make lib-clang-asan && make bin-clang-lstatic
  • make lib-gcc-asan && make bin-gcc-stat-lstatic

3

In this case sanitizer alswo works well.

  • make lib-gcc-asan && make bin-gcc-preload (output presented)

Motivated by this gist.

#ifdef DYNAMIC
#include <dlfcn.h>
#elif STATIC
#include "my.h"
#endif
#include <iostream>
int main(int argc, char **argv) {
#ifdef DYNAMIC
std::cout << "DYNAMIC option\n";
void* handle = dlopen("./libmy.so", RTLD_LAZY);
if (!handle) {
std::cerr << "Can not open library: " << dlerror() << '\n';
return 1;
}
std::cout << "Loading symbol 'sayHi'...\n";
typedef int (*say_hi_t)(int);
say_hi_t sayHi = (say_hi_t) dlsym(handle, "sayHi");
if (!sayHi) {
std::cerr << "Can not load symbol 'sayHi': " << dlerror() << '\n';
dlclose(handle);
return 1;
}
#elif STATIC
std::cout << "STATIC option\n";
#endif
int argument = argc + 100;
int res = sayHi(argument); // Boom.
std::cout << "sayHi(" << argument << ") = " << res << '\n';
return 0;
}
SAN_CMP := -fno-omit-frame-pointer -fsanitize=address
SAN_STAT := -static-libstdc++ -static-libasan
BIN_CMP := -std=c++11 -c -I. main.cpp -O -g3 -o main.o
LIB_CMP := -std=c++11 my.cpp -o libmy.so -shared -fPIC -g3
RUN := LD_LIBRARY_PATH=. ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=$(shell which llvm-symbolizer)
PRELOAD := LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libasan.so.1
lib-gcc:
g++-4.9 $(LIB_CMP)
lib-gcc-asan:
g++-4.9 $(SAN_CMP) $(LIB_CMP)
lib-clang-asan:
clang++-3.8 $(SAN_CMP) $(LIB_CMP)
bin-gcc-ldynamic:
g++-4.9 $(SAN_CMP) $(BIN_CMP) -DDYNAMIC
g++-4.9 -o main-gcc-ldynamic main.o -ldl -lasan
$(RUN) ./main-gcc-ldynamic
bin-gcc-lstatic:
g++-4.9 $(SAN_CMP) $(BIN_CMP) -DSTATIC
g++-4.9 -o main-gcc-lstatic main.o -L. -lmy -lasan
$(RUN) ./main-gcc-lstatic
bin-gcc-stat-lstatic:
g++-4.9 $(SAN_CMP) $(BIN_CMP) -DSTATIC
g++-4.9 $(SAN_CMP) $(SAN_STAT) -o main-gcc-stat-lstatic main.o -L. -lmy
$(RUN) ./main-gcc-stat-lstatic
bin-gcc-preload:
g++-4.9 $(BIN_CMP) -DDYNAMIC
g++-4.9 -o main-gcc-preload main.o -L. -ldl -lmy
$(RUN) $(PRELOAD) ./main-gcc-preload
bin-clang-ldynamic:
clang++-3.8 $(SAN_CMP) $(BIN_CMP) -DDYNAMIC
clang++-3.8 $(SAN_CMP) -o main-clang-ldynamic main.o -L. -lmy
$(RUN) ./main-clang-ldynamic
bin-clang-lstatic:
clang++-3.8 $(SAN_CMP) $(BIN_CMP) -DSTATIC
clang++-3.8 $(SAN_CMP) -o main-clang-lstatic main.o -L. -lmy
$(RUN) ./main-clang-lstatic
clean:
rm -f libmy.so
rm -f main-*
rm -f *.o
#include "my.h"
int sayHi(int n) {
int array_at_libmy[100];
array_at_libmy[1] = 0;
return array_at_libmy[n]; // Buggy access.
}
#pragma once
extern "C" int sayHi(int n);
$ make lib-gcc-asan && make bin-gcc-lstatic
g++-4.9 -fno-omit-frame-pointer -fsanitize=address -std=c++11 my.cpp -o libmy.so -shared -fPIC -g3
g++-4.9 -fno-omit-frame-pointer -fsanitize=address -std=c++11 -c -I. main.cpp -O -g3 -o main.o -DSTATIC
g++-4.9 -o main-gcc-lstatic main.o -L. -lmy -lasan
LD_LIBRARY_PATH=. ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer ./main-gcc-lstatic
STATIC option
=================================================================
==5029==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff37365794 at pc 0x7fd1553aa84f bp 0x7fff373655c0 sp 0x7fff373655b8
READ of size 4 at 0x7fff37365794 thread T0
#0 0x7fd1553aa84e in sayHi /home/menato/Work/tmp/my.cpp:6
#1 0x400c86 in main /home/menato/Work/tmp/main.cpp:40
#2 0x7fd153da982f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x400b28 in _start (/home/menato/Work/tmp/main-gcc-lstatic+0x400b28)
Address 0x7fff37365794 is located in stack of thread T0 at offset 436 in frame
#0 0x7fd1553aa77f in sayHi /home/menato/Work/tmp/my.cpp:3
This frame has 1 object(s):
[32, 432) 'array_at_libmy' <== Memory access at offset 436 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/menato/Work/tmp/my.cpp:6 sayHi
Shadow bytes around the buggy address:
0x100066e64aa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100066e64ab0: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
0x100066e64ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100066e64ad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100066e64ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100066e64af0: 00 00[f4]f4 f3 f3 f3 f3 00 00 00 00 f1 f1 f1 f1
0x100066e64b00: 01 f4 f4 f4 f3 f3 f3 f3 00 00 00 00 00 00 00 00
0x100066e64b10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100066e64b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100066e64b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100066e64b40: 00 00 00 00 00 00 00 00 00 00 00 00 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 right 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
Contiguous container OOB:fc
ASan internal: fe
==5029==ABORTING
Makefile:25: recipe for target 'bin-gcc-lstatic' failed
make: *** [bin-gcc-lstatic] Error 1
$ make lib-gcc-asan && make bin-gcc-preload
g++-4.9 -fno-omit-frame-pointer -fsanitize=address -std=c++11 my.cpp -o libmy.so -shared -fPIC -g3
g++-4.9 -std=c++11 -c -I. main.cpp -O -g3 -o main.o -DDYNAMIC
g++-4.9 -o main-gcc-preload main.o -L. -ldl -lmy
LD_LIBRARY_PATH=. ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libasan.so.1 ./main-gcc-preload
DYNAMIC option
Loading symbol 'sayHi'...
=================================================================
==5698==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc22f72144 at pc 0x7f39afaf784f bp 0x7ffc22f71f70 sp 0x7ffc22f71f68
READ of size 4 at 0x7ffc22f72144 thread T0
#0 0x7f39afaf784e in sayHi /home/menato/Work/tmp/my.cpp:6
#1 0x400ad6 in main /home/menato/Work/tmp/main.cpp:40
#2 0x7f39b275c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x400928 in _start (/home/menato/Work/tmp/main-gcc-preload+0x400928)
Address 0x7ffc22f72144 is located in stack of thread T0 at offset 436 in frame
#0 0x7f39afaf777f in sayHi /home/menato/Work/tmp/my.cpp:3
This frame has 1 object(s):
[32, 432) 'array_at_libmy' <== Memory access at offset 436 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/menato/Work/tmp/my.cpp:6 sayHi
Shadow bytes around the buggy address:
0x1000045e63d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000045e63e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000045e63f0: 00 00 f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00
0x1000045e6400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000045e6410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000045e6420: 00 00 00 00 00 00 00 00[f4]f4 f3 f3 f3 f3 00 00
0x1000045e6430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000045e6440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000045e6450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000045e6460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000045e6470: 00 00 00 00 00 00 00 00 00 00 00 00 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 right 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
Contiguous container OOB:fc
ASan internal: fe
==5698==ABORTING
Makefile:35: recipe for target 'bin-gcc-preload' failed
make: *** [bin-gcc-preload] Error 1
$ make lib-gcc && make bin-gcc-lstatic
g++-4.9 -std=c++11 my.cpp -o libmy.so -shared -fPIC -g3
g++-4.9 -fno-omit-frame-pointer -fsanitize=address -std=c++11 -c -I. main.cpp -O -g3 -o main.o -DSTATIC
g++-4.9 -o main-gcc-lstatic main.o -L. -lmy -lasan
LD_LIBRARY_PATH=. ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer ./main-gcc-lstatic
STATIC option
sayHi(101) = 32766
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment