Ceci est une ancienne révision du document !
Table des matières
Sanitizers
Pour que les sanitizers fonctionnent bien, il est très fortement recommandé (imposé par Visual Studio) de compiler le projet et toutes ces dépendances.
memory
- main.c
int main(int argc, char **argv) { int x[10]; x[0] = 1; return x[argc]; }
clang -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 -g main.c -o main
$ clang -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 -g main5.c -o main5 && ./main5==3981==WARNING: MemorySanitizer: use-of-uninitialized-value #0 0x492d5e in main /tmp/main5.c:4:3 #1 0x7f592c432461 in __libc_start_main .../sys-libs/glibc-2.25-r9/work/glibc-2.25/csu/../csu/libc-start.c:295 #2 0x41a129 in _start (/tmp/main5+0x41a129) SUMMARY: MemorySanitizer: use-of-uninitialized-value /tmp/main5.c:4:3 in main
valgrind détecte l'erreur.
==4011== Syscall param exit_group(status) contains uninitialised byte(s) ==4011== at 0x4F002B8: _Exit (_exit.c:31) ==4011== by 0x4E70423: __run_exit_handlers (exit.c:98) ==4011== by 0x4E704DC: exit (exit.c:105) ==4011== by 0x4E58468: (below main) (libc-start.c:329) ==4011== Uninitialised value was created by a stack allocation ==4011== at 0x400470: main (main5.c:1)
Instrumentation de la stdlib :
L'utilisation de la libraire standard crée de nombreux faux positifs si la stdlib n'est pas instrumentée.
Pour l'instrumenter, il faut suivre les instructions de MemorySanitizerLibcxxHowTo Archive du 29/01/2016 le 13/02/2020
Ma tentative :
- unwind de gcc sans sanitize : Building libunwind Archive du 29/01/2019 le 13/02/2020
- libc++ and libc++abi avec -fsanitizer (MemorySanitizerLibcxxHowTo). Ajouter
LDFLAGS="-L …/llvm-project/build/lib/clang/8.0.0/x86_64-unknown-linux-gnu/lib -lunwind"
si nécessaire. - compilation de poppler avec libc++ de clang https://github.com/google/oss-fuzz/blob/master/projects/poppler/build.sh
LDFLAGS="-L .../llvm-project/build/lib/clang/8.0.0/x86_64-unknown-linux-gnu/lib -lunwind -L.../llvm-project_libcxx_msan/build/lib -lc++abi" CFLAGS="-fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 -stdlib=libc++ -I.../llvm-project_libcxx_msan/build/include -I.../llvm-project_libcxx_msan/build/include/c++/v1" CXXFLAGS="-fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 -stdlib=libc++ -I.../llvm-project_libcxx_msan/build/include -I.../llvm-project_libcxx_msan/build/include/c++/v1" cmake .. -DCMAKE_BUILD_TYPE=debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
- Compilation du programme de test sans succès :
clang++ -L .../llvm-project/build/lib/clang/8.0.0/x86_64-unknown-linux-gnu/lib -lunwind -L .../llvm-project_libcxx_msan/build/lib -lc++abi -fsanitize=memory,fuzzer -stdlib=libc++ -I.../llvm-project_libcxx_msan/build/include -I.../llvm-project_libcxx_msan/build/include/c++/v1 -I ~/info/programmation/poppler/cpp/ pdf_fuzzer.cc -o fuzz_target -L ~/info/programmation/poppler/build/ -lpoppler -L ~/info/programmation/poppler/build/cpp/ -lpoppler-cpp
Control Flow Integrity
Sanitize, Fuzz, and Harden Your C++ Code, Archive
Nécessite l'option Gold
de llvm
.
Il est possible d'activer tous les cfi-* (cfi-vcall
, cfi-ncall
, cfi-icall
, cfi-derived-cast
, cfi-unrelated-cast
) en une seule fois : -fsanitize=cfi
.
Exemple 1
- a.c
#include <cstdio> void Bad() { puts("BOOO"); } struct Expr { long a[2]; long (*Op)(long *); }; int main(int argc, char **argv) { struct Expr e; // On écrit indirectement sur la variable Op. e.a[2 * argc] = (long)&Bad; e.Op(e.a); }
- Sans le sanitizer
clang a.c && ./a.out
BOOO
- Avec le sanitizer
clang -flto -fsanitize=cfi -fvisibility=hidden -fno-sanitize-trap=cfi -g a.c && ./a.out
file.cpp:11:3: runtime error: control flow integrity check for type 'long (long *)' failed during indirect function call file.cpp:2: note: Bad() defined here SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior cfi_cast_strict.cpp:11:3 in
valgrind ne détecte pas l'erreur.
Exemple 2
- a.c
void Bad() { puts("BOOO"); exit(0); } int main(int argc, char **argv) { long array[10]; array[argc * 13] = (long)&Bad; }
Nécessite l'option Gold
de llvm
. L'exemple ne marche que pour du 64 bits.
- Sans protection
clang a.c && ./a.out
BOOO Segmentation fault
- Avec protection
safe-stack
clang -fsanitize=safe-stack a.c && ./a.out
Pas de message d'erreur...
- Avec protection
cfi
clang++ cfi_cast_strict.cpp -O0 -fsanitize=cfi -flto=thin -fvisibility=hidden -fno-sanitize-trap=cfi
BOOO UndefinedBehaviorSanitizer:DEADLYSIGNAL ==12856==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x7f6e125389a0 (pc 0x7f6e125389a0 bp 0x7ffcbb5f9370 sp 0x7ffcbb5f92a8 T12856) ==12856==The signal is caused by a READ memory access. ==12856==Hint: PC is at a non-executable region. Maybe a wild jump? #0 0x7f6e125389a0 (/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/libstdc++.so.6+0x2209a0) UndefinedBehaviorSanitizer can not provide additional info. SUMMARY: UndefinedBehaviorSanitizer: SEGV (/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/libstdc++.so.6+0x2209a0) ==12856==ABORTING
valgrind détecte l'erreur.
Erreurs
runtime error: control flow integrity check for type '...' failed during cast to unrelated type (vtable address 0x000000000000)
Le fait que l'adresse de la vtable
soit nulle indique que la classe n'en a pas. Si une ou plusieurs méthodes sont déclarés virtuelles, elles ont été optimisées par le compilateur.
La solution est donc de supprimer (après vérification) la déclaration virtual
.
Issue 515973: Invalid cast in SkTArray.h Archive du 31/07/2015 le 20/06/2021
Unified Diff: include/core/SkTArray.h Archive du 31/07/2015 le 20/06/2021
cast to unrelated type aligned_buffer.h
/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/include/g++-v11/ext/aligned_buffer.h:115:16: runtime error: control flow integrity check for type '...' failed during cast to unrelated type (vtable address 0x747365742f617461) 0x747365742f617461: note: invalid vtable <memory cannot be printed> /usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/include/g++-v11/ext/aligned_buffer.h:115:16: note: check failed in ..., vtable located in (unknown) SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/include/g++-v11/ext/aligned_buffer.h:115:16 in
L'utilisation de std::make_shared
couplé avec le -fsanitize=cfi
peu poser problème. Le constructeur de shared_ptr
va vouloir caster la zone mémoire avant d'appeler le constructeur. La zone mémoire étant non initialisée, le sanitizer ne va pas reconnaitre le pointeur vtable
.
Ce problème existe dans l'implémentation sous clang et gcc.
clang
a résolu le problème en ajoutant _LIBCPP_NO_CFI
à la fonction _Storage::__get_elem
dans le fichier memory
.
Pour gcc
, il faut faire la même chose avec __attribute__((__no_sanitize__("cfi")))
pour les fonctions __aligned_buffer::_M_ptr
dans le fichier aligned_buffer.h
.
New: Recent shared_ptr storage change causes CFI cast failures during make_shared Archive du 01/02/2021 le 23/06/2021
Avoid cast<T*> before T is constructed to pacify CFI checks Archive du 01/02/2021 le 23/06/2021
Disable CFI in __get_elem to allow casting a pointer to uninitialized memory Archive du 04/02/2021 le 23/06/2021
RTTI symbol not found for class
Pour diagnostiquer une erreur de sanitizer, il est possible de mettre un point d'arrêt avec gdb.
Il est possible d'avoir des warnings de la part de gdb à propos de RTTI (warning: RTTI symbol not found for class
). Dans ce cas, le plantage du sanitizer cfi est probablement un faux positif car il travaille sur les informations RTTI.
Exemple de message d'erreur généré par le sanitizer à l'exécution.
/usr/lib/gcc/x86_64-pc-linux-gnu/11.2.1/include/g++-v11/bits/std_function.h:590:9: runtime error: control flow integrity check for type 'std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (const std::_Any_data &)' failed during indirect function call /usr/lib/gcc/x86_64-pc-linux-gnu/11.2.1/include/g++-v11/bits/std_function.h:289: note: std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_State_baseV2::_Setter<std::shared_ptr<restbed::Response>, std::shared_ptr<restbed::Response> const&> >::_M_invoke(std::_Any_data const&) defined here /usr/lib/gcc/x86_64-pc-linux-gnu/11.2.1/include/g++-v11/bits/std_function.h:590:9: note: check failed in /mnt/c/j/build/clang_cfi/test/backend/data/test_load_vertical_eccentric, destination function located in /mnt/c/j/build/clang_cfi/_deps/restbed-build/librestbed.so.4 SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/lib/gcc/x86_64-pc-linux-gnu/11.2.1/include/g++-v11/bits/std_function.h:590:9 in
Dans cet exemple, la classe restbed::Response
a bien été compilée avec l'option __attribute__((visibility ("default")))
.
Ces erreurs peuvent apparaître quand le sanitizer travaille avec une librairie dynamique. Pour que le sanitizer cfi réussisse son travail, il faut que les librairies compilées avec le sanitizer cfi soient liées statiquement.
ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
Comme le titre l'indique, il faut définir LD_PRELOAD
. Cela peut être le cas si un module python utilise une extension en C++ qui a été compilé via un sanitizer.
export LD_PRELOAD=$(g++ -print-file-name=libasan.so)
Using memory sanitizer (asan) on C/C++ library loaded to python with ctypes Archive du 26/03/2023 le 15/11/2023
AddressSanitizer: container-overflow on address avec std::vector
Il faut compiler le projet et toutes ses dépendances avec les sanitizers. Sinon, on se retrouve avec le message d'erreur suivant:
==42==ERROR: AddressSanitizer: container-overflow on address 0x14a00f3f at pc 0x0067b070 bp 0x12affe70 sp 0x12affe7c READ of size 1 at 0x14a00f3f thread T0 #0 0x67b070 in std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::__is_long[abi:ne180100]() const ../emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/string:1773:29 #1 0x6bc47b in std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::basic_string[abi:ne180100](std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>&&)::'lambda'(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>&)::operator()(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>&) const ../emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/string:925:70 #2 0x6bc341 in std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>::basic_string[abi:ne180100](std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>&&) ../emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/string:925:14 #3 0x6bbf82 in void std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>::construct[abi:ne180100]<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>&&) ../emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/__memory/allocator.h:173:24 #4 0x6bba85 in void std::__2::allocator_traits<std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>>::construct[abi:ne180100]<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>, void>(std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>&, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*, std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>&&) ../emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/__memory/allocator_traits.h:296:9 #5 0x6ba234 in std::__2::reverse_iterator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*> std::__2::__uninitialized_allocator_move_if_noexcept[abi:ne180100]<std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>, std::__2::reverse_iterator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*>, std::__2::reverse_iterator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*>, std::__2::reverse_iterator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*>>(std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>&, std::__2::reverse_iterator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*>, std::__2::reverse_iterator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*>, std::__2::reverse_iterator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>*>) ../emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/__memory/uninitialized_algorithms.h:612:5 #6 0x6b8bfa in std::__2::vector<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>, std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>>::__swap_out_circular_buffer(std::__2::__split_buffer<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>, std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>&>&) ../emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/vector:990:20 #7 0x4e7a1 in std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>* std::__2::vector<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>, std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>>::__push_back_slow_path<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>(std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>&&) ../emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/vector:1455:3 #8 0x4e241 in std::__2::vector<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>, std::__2::allocator<std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>>>::push_back[abi:nn180100](std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char>>&&) ../emsdk/upstream/emscripten/cache/sysroot/include/c++
AddressSanitizerContainerOverflow Archive du 31/03/2017 le 12/11/2024
gdb
Pour arrêter le debug lors d'une erreur, on peut utiliser les symboles :
__ubsan::ScopedReport::~ScopedReport __tsan::ReportRace
How can I break on UBSan reports in gdb and continue? Archive du 12/06/2015 le 18/04/2021