Ceci est une ancienne révision du document !
Table des matières
Status
Installation
Ubuntu
- Utilisation des derniers paquets
Ajouté les clés de vérification de l'authenticité en exécutant :
cd /usr/share/keyrings sudo wget https://apt.llvm.org/llvm-snapshot.gpg.key
Key is stored in legacy trusted.gpg keyring after Ubuntu 22.04 update Archive du 21/04/2022 le 18/10/2022
Ajouter à /etc/apt/sources.list
:
deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg.key] http://apt.llvm.org/unstable/ llvm-toolchain main deb-src [signed-by=/usr/share/keyrings/llvm-snapshot.gpg.key] http://apt.llvm.org/unstable/ llvm-toolchain main # 15 deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg.key] http://apt.llvm.org/unstable/ llvm-toolchain-15 main deb-src [signed-by=/usr/share/keyrings/llvm-snapshot.gpg.key] http://apt.llvm.org/unstable/ llvm-toolchain-15 main
La première paire de lignes est globale et doit toujours être ajoutée.
La seconde paire est pour ajouter la version précise.
Puis faire un apt update
/ apt upgrade
.
LLVM Debian/Ubuntu nightly packages Archive du 31/07/2022 le 18/10/2022
Install LLVM on Ubuntu 22.04 Archive du 01/12/2017 le 18/10/2022
- Installation en parallèle
Ubuntu ne fournit pas de méthode 100% automatique. Il faut passer manuellement par update-alternatives
.
- update-alternatives-clang.sh
#!/usr/bin/env bash update_alternatives() { local version=${1} local priority=${2} local master=${3} local slaves=${4} local path=${5} local cmdln cmdln="--verbose --install ${path}${master} ${master} ${path}${master}-${version} ${priority}" for slave in ${slaves}; do cmdln="${cmdln} --slave ${path}${slave} ${slave} ${path}${slave}-${version}" done sudo update-alternatives ${cmdln} } if [[ ${#} -ne 2 ]]; then echo usage: "${0}" clang_version priority exit 1 fi version=${1} priority=${2} path="/usr/bin/" master="llvm-config" slaves="llvm-addr2line llvm-ar llvm-as llvm-bcanalyzer llvm-bitcode-strip llvm-cat llvm-cfi-verify llvm-cov llvm-c-test llvm-cvtres llvm-cxxdump llvm-cxxfilt llvm-cxxmap llvm-debuginfod llvm-debuginfod-find llvm-diff llvm-dis llvm-dlltool llvm-dwarfdump llvm-dwarfutil llvm-dwp llvm-exegesis llvm-extract llvm-gsymutil llvm-ifs llvm-install-name-tool llvm-jitlink llvm-jitlink-executor llvm-lib llvm-libtool-darwin llvm-link llvm-lipo llvm-lto llvm-lto2 llvm-mc llvm-mca llvm-ml llvm-modextract llvm-mt llvm-nm llvm-objcopy llvm-objdump llvm-omp-device-info llvm-opt-report llvm-otool llvm-pdbutil llvm-PerfectShuffle llvm-profdata llvm-profgen llvm-ranlib llvm-rc llvm-readelf llvm-readobj llvm-reduce llvm-remark-size-diff llvm-rtdyld llvm-sim llvm-size llvm-split llvm-stress llvm-strings llvm-strip llvm-symbolizer llvm-tapi-diff llvm-tblgen llvm-tli-checker llvm-undname llvm-windres llvm-xray" update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}" master="clang" slaves="analyze-build asan_symbolize bugpoint c-index-test clang++ clang-apply-replacements clang-change-namespace clang-check clang-cl clang-cpp clangd clang-doc clang-extdef-mapping clang-format clang-format-diff clang-include-fixer clang-linker-wrapper clang-move clang-nvlink-wrapper clang-offload-bundler clang-offload-packager clang-offload-wrapper clang-pseudo clang-query clang-refactor clang-rename clang-reorder-fields clang-repl clang-scan-deps clang-tidy count diagtool dsymutil FileCheck find-all-symbols git-clang-format hmaptool hwasan_symbolize intercept-build ld64.lld ld.lld llc lld lldb lldb-argdumper lldb-instr lldb-server lldb-vscode lld-link lli lli-child-target modularize not obj2yaml opt pp-trace run-clang-tidy sancov sanstats scan-build scan-build-py scan-view split-file UnicodeNameMappingGenerator verify-uselistorder wasm-ld yaml2obj yaml-bench" update_alternatives "${version}" "${priority}" "${master}" "${slaves}" "${path}"
Utilisation pour enregistrer les alternatives : ./update-alternatives-clang.sh 16 16
update-alternatives-clang.sh Archive du 18/11/2017 le 18/10/2022
Utiliser pour switcher entre les versions :
sudo update-alternatives --config llvm-config sudo update-alternatives --config clang
How to Use update-alternatives Command on Ubuntu Archive du 01/05/2022 le 18/10/2022
Sanitizer
En plus de ceux commun avec gcc, il existe memory
.
memory
- main.c
int main(int argc, char **argv) { int x[10]; x[0] = 1; return x[argc]; }
clang -fsanitize=memory -g main.c -o main
$ clang -fsanitize=memory -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 -stdlib=libc++ -I.../llvm-project_libcxx_msan/build/include -I.../llvm-project_libcxx_msan/build/include/c++/v1" CXXFLAGS="-fsanitize=memory -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
.
- 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.
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.
Pile
- 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.
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
Fuzzer
Super Awesome Fuzzing, Part One Archive du 22/06/2017 le 13/02/2020
Il faut créer un fichier source qui ne contient qu'une seule fonction. La fonction main
est dans l'archive statique libFuzzer.a
.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
L'option de compilation -fsanitize=fuzzer
est obligatoire mais il peut être utile de rajouter -fno-omit-frame-pointer -g -fsanitize=address,undefined
.
clang++ -fno-omit-frame-pointer -g -fsanitize=address,undefined,fuzzer fuzz_me.cc
Si vous avez le message : error adding symbols: DSO missing from command line
, il faut rajouter -lgcc_s
.
Pour aller plus loin, même si ça possède de nombreuses fonctionnalités, je préfère afl mais une étude comparative va finir par s'imposer. Archive du 27/09/2020 le 13/02/2020
Options
-ftime-trace
Affiche où clang passe du temps pour compiler. Le fichier .json
généré s'ouvre avec Chrome chrome://tracing
time-trace: timeline / flame chart profiler for Clang Archive du 16/01/2019 le 11/11/2019