Coś co chciałem wypróbować już bardzo dawno temu, a mianowicie narzędzie do statycznej i dynamicznej analizy kodu/programów. Zachęcił mnie do tego przede wszystkim wykład z
GoginNative 2013: The Care and Feeding of C++’s Dragons (0:51h, 1:05h). Najwyraźniej funkcjonalność którą dostarcza kompilator (clang), została wydzielona do osobnego narzędzia jakim jest
scan-build, choć nie eksperymentowałem z tym rozwiązaniem za dużo. Kilka przydatnych linków:
Clang Static Analyzer
Narzędzie korzysta ze zbioru reguł, które wciąż się rozrastają. Można skorzystać z domyślnych ustawień lub samodzielnie wskazać pod jakim kątem ma być przeskanowany kod. Przykład z dzieleniem przez zero, zaczerpnięty z dokumentacji.
#include <iostream>
using namespace std;
int test(int z) {
if (z == 0)
return 1 / z;
return -1;
}
int main() {
int i = 63;
std::cin >> i;
test(i);
return 0;
}
W pierwszych dwóch linijkach dwa alternatywne wywołania:
clang++ -std=c++14 --analyze -Xanalyzer -analyzer-output=text main.cpp
# inna wersja
clang++ -std=c++14 --analyze -Xanalyzer -analyzer-checker=core.DivideZero -Xanalyzer -analyzer-output=text main.cpp
Wynik:
main.cpp:7:18: warning: Division by zero
return 1 / z;
^
main.cpp:14:10: note: Passing value via 1st parameter 'z'
test(i);
^
main.cpp:14:5: note: Calling 'test'
test(i);
^~~~~~~
main.cpp:6:9: note: Assuming 'z' is equal to 0
if (z == 0)
^~~~~~
main.cpp:6:5: note: Taking true branch
if (z == 0)
^
main.cpp:7:18: note: Division by zero
return 1 / z;
~~^~~
1 warning generated.
Niestety przykłady które stworzyłem samodzielnie, już nie podają takich wyników. Testy przeprowadzałem na wersji 3.6.0 oraz 3.8.0. No nic, rozwiązanie i tak jest interesujące, liczę że w przyszłości narzędzie się rozwinie.
#include <iostream>
using namespace std;
int test2(int z) {
return 1 / z;
}
int test3(int z) {
if (z > 15)
return 1;
return 1 / z;
}
int main() {
int i = 63;
std::cin >> i;
test2(i);
test3(i);
return 0;
}
Sanitizer
Innym narzędziem, które istnieje w clang-u jest sanitizer, a właściwie ich kolekcja. Analiza przeprowadzana jest dynamicznie, więc najlepsze efekty można uzyskać, jeżeli kod był tworzony razem z testami. Sprawdziłem jedynie
AddressSanitizer, ale w dokumentacji można odnaleźć informacji o innych:
#include <iostream>
using namespace std;
int main() {
char tab1[5];
for(int i = 0; i <= 5; ++i)
tab1[i] = 'x';
return 0;
}
Wywołanie:
clang++ -std=c++14 -fsanitize=address main.cpp
./a.out
Rezultat:
=================================================================
==5748==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd47e300b5 at pc 0x0000004dc5ad bp 0x7ffd47e30010 sp 0x7ffd47e30008
WRITE of size 1 at 0x7ffd47e300b5 thread T0
#0 0x4dc5ac (/home/beru/synetizer_test/a.out+0x4dc5ac)
#1 0x7fcefcca4a3f (/lib/x86_64-linux-gnu/libc.so.6+0x20a3f)
#2 0x4350e8 (/home/beru/synetizer_test/a.out+0x4350e8)
Address 0x7ffd47e300b5 is located in stack of thread T0 at offset 53 in frame
#0 0x4dc32f (/home/beru/synetizer_test/a.out+0x4dc32f)
This frame has 3 object(s):
[32, 36) ''
[48, 53) 'tab1' <== Memory access at offset 53 overflows this variable
[80, 84) 'i'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
Shadow bytes around the buggy address:
0x100028fbdfc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100028fbdfd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100028fbdfe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100028fbdff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100028fbe000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100028fbe010: f1 f1 f1 f1 04 f2[05]f2 f2 f2 04 f3 00 00 00 00
0x100028fbe020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100028fbe030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100028fbe040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100028fbe050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100028fbe060: 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
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==5748==ABORTING
Brak komentarzy:
Prześlij komentarz