Outils pour utilisateurs

Outils du site


prog:clang

Ceci est une ancienne révision du document !


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 :

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

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

Utilitaires

prog/clang.1683706449.txt.gz · Dernière modification : 2023/05/10 10:14 de root