Les Sanitizer
Les sanitizers sont des mécanismes d’instrumentation ajoutés à la compilation pour détecter à l’exécution des classes de bugs qui, sans cela, resteraient silencieuses ou difficiles à reproduire.
Chaque sanitizer cible une famille de problèmes bien précise : fuites mémoire, dépassements de tampon, mémoire non initialisée, comportements indéfinis ou violations de typage.
Leur principe général consiste à modifier le code généré par le compilateur afin d’ajouter des vérifications supplémentaires autour des opérations sensibles : accès mémoire, calculs arithmétiques, conversions de type ou manipulation de pointeurs. Ces vérifications s’exécutent ensuite en même temps que le programme instrumenté.
Certains sanitizers s’appuient sur une shadow memory, c’est-à-dire une zone mémoire parallèle utilisée pour mémoriser l’état des octets manipulés par le programme : adressables ou non, initialisés ou non, associés à un type précis, etc. C’est ce mécanisme qui permet par exemple à ASan, MSan ou TySan de détecter des erreurs au moment exact où elles surviennent.
Enfin, les sanitizers ne remplacent pas un débogueur ou une relecture du code : ils servent surtout à rendre visibles des comportements qui passeraient sinon inaperçus. En contrepartie, ils augmentent le coût d’exécution et nécessitent souvent de recompiler l’ensemble du programme avec les bonnes options.
Leak Sanitizer (LSAN)
LeakSanitizer vise les allocations dynamiques qui n’ont pas été libérées avant la fin du programme. Il est particulièrement utile pour repérer les fuites simples dans des cas où le processus se termine normalement mais laisse de la mémoire allouée.
Dans l’exemple ci-dessous, un bloc de 10 octets est alloué puis perdu sans appel
à free. LSan signale alors une fuite directe et fournit la pile
d’appels de l’allocation concernée.
// leak.c
#include <stdlib.h>
void main() {
void *a = malloc(10);
}
$ gcc -fsanitize=leak leak.c -o leak
$ ./leak
=================================================================
==10823==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 10 byte(s) in 1 object(s) allocated from:
#0 0x7f39cac16c25 in malloc ../../../../src/libsanitizer/lsan/lsan_interceptors.cpp:75
#1 0x5620b2e7414a in main (/home/galas/HEIG-VD/SSE/src/C/leak/leak+0x114a) (BuildId: fe97d76787921889785b1f121ad4846b9e02f45f)
#2 0x7f39caa35ca7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
SUMMARY: LeakSanitizer: 10 byte(s) leaked in 1 allocation(s).
Address Sanitizer (ASAN)
AddressSanitizer détecte les erreurs d’accès mémoire : dépassement de tampon sur pile ou sur tas, use-after-free, use-after-scope et divers accès hors limites. Il ajoute pour cela une shadow memory et des red zones autour des objets suivis.
Dans cet exemple, le tableau local a une taille de 4 octets. L’accès avec l’index 5 produit une lecture hors borne sur la pile, immédiatement détectée et documentée par ASan.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int index = atoi(argv[1]);
char a[4] = {0x41,0x42,0x43,0x44};
putchar(a[index]);
return 0;
}
$ gcc -fsanitize=address stack_buffer_overflow.c -o stack
$ ./stack 1
B
$ ./stack 5
=================================================================
==11164==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f1189d00025 at pc 0x556d2b9412cd bp 0x7fff67b3a910 sp 0x7fff67b3a908
READ of size 1 at 0x7f1189d00025 thread T0
#0 0x556d2b9412cc in main (/home/galas/HEIG-VD/SSE/src/C/tampon/stack+0x12cc) (BuildId: 5d79e89407d754ca8412e7abfd6aa64e067b398c)
#1 0x7f118be35ca7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#2 0x7f118be35d64 in __libc_start_main_impl ../csu/libc-start.c:360
#3 0x556d2b9410e0 in _start (/home/galas/HEIG-VD/SSE/src/C/tampon/stack+0x10e0) (BuildId: 5d79e89407d754ca8412e7abfd6aa64e067b398c)
Address 0x7f1189d00025 is located in stack of thread T0 at offset 37 in frame
#0 0x556d2b9411b8 in main (/home/galas/HEIG-VD/SSE/src/C/tampon/stack+0x11b8) (BuildId: 5d79e89407d754ca8412e7abfd6aa64e067b398c)
This frame has 1 object(s):
[32, 36) 'a' (line 6) <== Memory access at offset 37 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/galas/HEIG-VD/SSE/src/C/tampon/stack+0x12cc) (BuildId: 5d79e89407d754ca8412e7abfd6aa64e067b398c) in main
Shadow bytes around the buggy address:
0x7f1189cffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7f1189cffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7f1189cffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7f1189cfff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7f1189cfff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7f1189d00000: f1 f1 f1 f1[04]f3 f3 f3 00 00 00 00 00 00 00 00
0x7f1189d00080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7f1189d00100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7f1189d00180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7f1189d00200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7f1189d00280: 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
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
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
==11164==ABORTING
Utilisation avec GDB
On peut combiner ASAN avec gdb en placant un breakpoint sur la fonction __sanitizer::Die et faire un backtrace pour retrouver précisément où se trouve l'erreur dans le code.
$ clang-20 -fsanitize=address stack_buffer_overflow.c -o stack
$ gdb ./stack
(gdb) b __sanitizer::Die
Breakpoint 1 at 0xf6e75: file build-llvm/tools/clang/stage2-bins/runtimes/runtimes-bins/compiler-rt/lib/sanitizer_common/sanitizer_termination.cpp, line 51.
(gdb) r 4
Starting program: /home/galas/HEIG-VD/SSE/src/C/tampon/stack 4
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
=================================================================
==15006==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7bfff5b00024 at pc 0x55555566887a bp 0x7fffffffda50 sp 0x7fffffffda48
READ of size 1 at 0x7bfff5b00024 thread T0
[Detaching after fork from child process 15007]
#0 0x555555668879 in main (/home/galas/HEIG-VD/SSE/src/C/tampon/stack+0x114879) (BuildId: 8366ee994fc59db1fabed9dbf0625c046aef180b)
#1 0x7ffff7c9eca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#2 0x7ffff7c9ed64 in __libc_start_main csu/../csu/libc-start.c:360:3
#3 0x555555580360 in _start (/home/galas/HEIG-VD/SSE/src/C/tampon/stack+0x2c360) (BuildId: 8366ee994fc59db1fabed9dbf0625c046aef180b)
Address 0x7bfff5b00024 is located in stack of thread T0 at offset 36 in frame
#0 0x55555566873f in main (/home/galas/HEIG-VD/SSE/src/C/tampon/stack+0x11473f) (BuildId: 8366ee994fc59db1fabed9dbf0625c046aef180b)
This frame has 1 object(s):
[32, 36) 'a' <== Memory access at offset 36 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/galas/HEIG-VD/SSE/src/C/tampon/stack+0x114879) (BuildId: 8366ee994fc59db1fabed9dbf0625c046aef180b) in main
Shadow bytes around the buggy address:
0x7bfff5affd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5affe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5affe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5afff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5afff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7bfff5b00000: f1 f1 f1 f1[04]f3 f3 f3 00 00 00 00 00 00 00 00
0x7bfff5b00080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5b00100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5b00180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5b00200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bfff5b00280: 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
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
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
==15006==ABORTING
Breakpoint 1, __sanitizer::Die() () at build-llvm/tools/clang/stage2-bins/runtimes/runtimes-bins/compiler-rt/lib/sanitizer_common/sanitizer_termination.cpp:51
warning: 51 build-llvm/tools/clang/stage2-bins/runtimes/runtimes-bins/compiler-rt/lib/sanitizer_common/sanitizer_termination.cpp: Aucun fichier ou dossier de ce nom
(gdb) bt
#0 __sanitizer::Die() () at build-llvm/tools/clang/stage2-bins/runtimes/runtimes-bins/compiler-rt/lib/sanitizer_common/sanitizer_termination.cpp:51
#1 0x0000555555629fa9 in ~ScopedInErrorReport () at build-llvm/tools/clang/stage2-bins/runtimes/runtimes-bins/compiler-rt/lib/asan/asan_report.cpp:193
#2 0x000055555562d517 in ReportGenericError () at build-llvm/tools/clang/stage2-bins/runtimes/runtimes-bins/compiler-rt/lib/asan/asan_report.cpp:508
#3 0x000055555562e20a in __asan_report_load1 () at build-llvm/tools/clang/stage2-bins/runtimes/runtimes-bins/compiler-rt/lib/asan/asan_rtl.cpp:128
#4 0x000055555566887a in main ()
(gdb)
Memory Sanitizer (MSAN)
MemorySanitizer cible les lectures de mémoire non initialisée. Il est particulièrement pertinent quand la valeur lue ne provoque pas immédiatement un crash, mais influence quand même le comportement du programme.
Dans ce cas, un entier est alloué puis lu sans avoir reçu de valeur préalable. MSan repère cette utilisation et signale précisément la lecture fautive.
#include <stdio.h>
#include <stdlib.h>
int main(){
int *mem = malloc(sizeof(int));
printf("%d\n", *mem);
return 0;
}
$ clang-20 -fsanitize=memory uninitialised.c -o uninitialised
$ ./uninitialised
==11382==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x558c1d63e7ab in main (/home/galas/HEIG-VD/SSE/src/C/struct/uninitialised+0xce7ab) (BuildId: acb50b7d46869c73cbc97eddc6981cfac02f65d4)
#1 0x7f973ed41ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#2 0x7f973ed41d64 in __libc_start_main csu/../csu/libc-start.c:360:3
#3 0x558c1d5a2300 in _start (/home/galas/HEIG-VD/SSE/src/C/struct/uninitialised+0x32300) (BuildId: acb50b7d46869c73cbc97eddc6981cfac02f65d4)
SUMMARY: MemorySanitizer: use-of-uninitialized-value (/home/galas/HEIG-VD/SSE/src/C/struct/uninitialised+0xce7ab) (BuildId: acb50b7d46869c73cbc97eddc6981cfac02f65d4) in main
Exiting
Undefiend Behaviour Sanitizer (UBSAN)
UBSan détecte différentes formes de comportement indéfini définies par le standard C ou C++. Cela inclut notamment les déréférencements invalides, les décalages hors borne, certains débordements signés ou encore les retours manquants dans des fonctions qui doivent produire une valeur.
L’intérêt de cet outil est de signaler des erreurs qui peuvent sinon être optimisées ou masquées par le compilateur. Ici, le cas étudié est un déréférencement de pointeur nul.
#include <stdio.h>
char deref(const char * p) {
return *p ;
}
int main(){
const char *str = "Bonjour";
printf("%c\n", deref("Salut"));
printf("%c\n", deref(str));
printf("%c\n", deref(NULL));
return 1;
}
$ gcc -fsanitize=undefined example.c -o example
$ ./example
S
B
example.c:4:12: runtime error: load of null pointer of type 'const char'
Erreur de segmentation
Mais si on regarde du coté de clang
Le point intéressant ici est que GCC et Clang ne présentent pas exactement le même niveau de détail dans le rapport. Les deux détectent l’erreur, mais Clang fournit généralement une sortie plus bavarde, avec davantage d’informations contextuelles sur le signal et l’état du programme.
$ clang-20 -fsanitize=undefined example.c -o example
$ ./example
S
B
example.c:4:12: runtime error: load of null pointer of type 'const char'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior example.c:4:12
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==11617==ERROR: UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000000 (pc 0x555d90dde526 bp 0x7ffe817195c0 sp 0x7ffe817195b0 T11617)
==11617==The signal is caused by a READ memory access.
==11617==Hint: address points to the zero page.
#0 0x555d90dde526 in deref (/home/galas/HEIG-VD/SSE/src/C/undefined/example+0x32526) (BuildId: 53c0531a5564c06d8b0ae67a4e8ebdeea92e8bc7)
#1 0x555d90dde591 in main (/home/galas/HEIG-VD/SSE/src/C/undefined/example+0x32591) (BuildId: 53c0531a5564c06d8b0ae67a4e8ebdeea92e8bc7)
#2 0x7f091676eca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#3 0x7f091676ed64 in __libc_start_main csu/../csu/libc-start.c:360:3
#4 0x555d90db13e0 in _start (/home/galas/HEIG-VD/SSE/src/C/undefined/example+0x53e0) (BuildId: 53c0531a5564c06d8b0ae67a4e8ebdeea92e8bc7)
==11617==Register values:
rax = 0x0000000000000000 rbx = 0x00007ffe817196f8 rcx = 0x0000000000000000 rdx = 0x0000000000000000
rdi = 0x00007ffe81718908 rsi = 0x0000000000000000 rbp = 0x00007ffe817195c0 rsp = 0x00007ffe817195b0
r8 = 0x000000000000007c r9 = 0x0000000000000000 r10 = 0x00000000ffffff00 r11 = 0x0000000000000246
r12 = 0x0000000000000000 r13 = 0x00007ffe81719708 r14 = 0x00007f0916acd000 r15 = 0x0000555d90df3668
UndefinedBehaviorSanitizer can not provide additional info.
SUMMARY: UndefinedBehaviorSanitizer: SEGV (/home/galas/HEIG-VD/SSE/src/C/undefined/example+0x32526) (BuildId: 53c0531a5564c06d8b0ae67a4e8ebdeea92e8bc7) in deref
==11617==ABORTING
Utilisation avec gdb
Avec UBSAN on peut brancher notre breakpoint sur __ubsan::Diag::~Diag qui est le destructeur de la fonction de diagnostic
$ gdb ./example
(gdb) b __ubsan::Diag::~Diag
Breakpoint 1 at 0x2b9b0
(gdb) command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>finish
>end
(gdb) run
Starting program: /home/galas/HEIG-VD/SSE/src/C/undefined/example
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
S
B
Breakpoint 1, 0x000055555557f9b0 in __ubsan::Diag::~Diag() ()
example.c:4:12: runtime error: load of null pointer of type 'const char'
0x0000555555581217 in handleTypeMismatchImpl(__ubsan::TypeMismatchData*, unsigned long, __ubsan::ReportOptions) ()
(gdb) bt
#0 0x0000555555581217 in handleTypeMismatchImpl(__ubsan::TypeMismatchData*, unsigned long, __ubsan::ReportOptions) ()
#1 0x0000555555580e88 in __ubsan_handle_type_mismatch_v1 ()
#2 0x0000555555586522 in deref ()
#3 0x0000555555586592 in main ()
(gdb)
Type Sanitizer (TySAN)
TypeSanitizer s’intéresse aux violations de typage dynamique, en particulier aux accès effectués via un type incompatible avec le type réel de l’objet stocké en mémoire.
Le cas présenté ici force une lecture d’un float à travers un
pointeur vers int. Le compilateur émet déjà un avertissement
de typage, puis TySan détecte à l’exécution une violation de type-aliasing.
#include <stdio.h>
int main() {
float f = 1.f;
int* i = &f;
printf("%d\n", *i);
return 0;
}
$ clang-20 -fsanitize=type transtypage.c -o transtypage
transtypage.c:4:10: warning: incompatible pointer types initializing 'int *' with an expression of type 'float *' [-Wincompatible-pointer-types]
4 | int* i = &f;
| ^ ~~
1 warning generated.
$ ./transtypage
==11753==ERROR: TypeSanitizer: type-aliasing-violation on address 0x7ffc283dfa78 (pc 0x5562fb9ffc35 bp 0x7ffc283df9f0 sp 0x7ffc283df980 tid 11753)
READ of size 4 at 0x7ffc283dfa78 with type int accesses an existing object of type float
#0 0x5562fb9ffc34 in main (/home/galas/HEIG-VD/SSE/src/C/type/transtypage+0x2bc34) (BuildId: f02429a063706baf61fc1563ff070cb0ae1e06c4)
1065353216
TySan reste un outil plus expérimental et plus ciblé que les autres sanitizers classiques. Il est surtout pertinent pour étudier les problèmes de strict aliasing, de transtypage et de non-conformité vis-à-vis des règles de type du langage.
Fuzzing
Le fuzzing consiste à exécuter automatiquement un programme avec un très grand nombre d’entrées générées ou mutées afin de provoquer des cas limites difficiles à atteindre manuellement.
Dans un contexte C ou C++, on ne fuzz pas directement un programme complet au hasard. On écrit généralement une fonction cible qui reçoit un buffer arbitraire, puis on laisse le moteur de fuzzing explorer l’espace des entrées.
Avec libFuzzer, cette fonction d’entrée doit respecter une signature précise :
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size).
Le moteur appelle ensuite cette fonction en boucle avec des données différentes.
Dans l’exemple suivant, le buffer fourni par le fuzzer est utilisé pour construire une valeur d’énumération. L’idée est simple : si une valeur invalide est injectée, UBSan pourra la signaler à l’exécution.
#include <cstdint>
#include <cstddef>
enum E { x, y, z };
int res(E e) {
switch (e) {
case x: return 10;
case y: return 20;
case z: return 30;
}
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 1) return 0;
E e = static_cast<E>(data[0]);
res(e);
return 0;
}
Ce petit programme ne contient pas de boucle explicite : c’est libFuzzer qui se charge de relancer la fonction cible encore et encore, en faisant varier les entrées. Le code à tester doit donc être isolé dans une fonction stable, rapide et déterministe.
La compilation combine ici deux éléments : fuzzer, qui fournit le moteur d’exécution et de mutation, et undefined, qui active UBSan pour transformer les comportements indéfinis en diagnostics visibles.
Le warning affiché par clang++ vient du fait que le fichier s’appelle
ubsan.c alors que son contenu est du C++. Le code compile tout de même,
mais une extension .cpp serait plus cohérente.
$ clang++-20 -fsanitize=undefined,fuzzer ubsan.c -o ubsan
clang++-20: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]
$ ./ubsan
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 646727359
INFO: Loaded 1 modules (13 inline 8-bit counters): 13 [0x555a3ef09fe8, 0x555a3ef09ff5),
INFO: Loaded 1 PC tables (13 PCs): 13 [0x555a3ef09ff8,0x555a3ef0a0c8),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
ubsan.c:19:9: runtime error: load of value 10, which is not a valid value for type 'E'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan.c:19:9
ubsan.c:7:13: runtime error: load of value 10, which is not a valid value for type 'E'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan.c:7:13
#2 INITED cov: 6 ft: 7 corp: 1/1b exec/s: 0 rss: 28Mb
#8 NEW cov: 9 ft: 10 corp: 2/2b lim: 4 exec/s: 0 rss: 28Mb L: 1/1 MS: 1 ChangeBit-
#18 NEW cov: 10 ft: 11 corp: 3/3b lim: 4 exec/s: 0 rss: 28Mb L: 1/1 MS: 5 ShuffleBytes-CopyPart-ChangeBit-ShuffleBytes-ChangeBinInt-
#55 NEW cov: 11 ft: 12 corp: 4/4b lim: 4 exec/s: 0 rss: 28Mb L: 1/1 MS: 2 ShuffleBytes-ChangeBit-
#8388608 pulse cov: 11 ft: 12 corp: 4/4b lim: 4096 exec/s: 2796202 rss: 28Mb
^C==13874== libFuzzer: run interrupted; exiting
Au lancement, libFuzzer initialise son moteur, crée un corpus minimal, puis commence à explorer différentes entrées. Dès qu’une nouvelle entrée augmente la couverture ou provoque un comportement intéressant, elle est conservée.
Ici, UBSan signale qu’une valeur invalide a été chargée pour le type E.
Cela signifie que le fuzzer a rapidement trouvé un octet en dehors des valeurs
autorisées par l’énumération.
Les lignes NEW, cov et corp indiquent que le
moteur progresse dans l’exploration du code. Le fuzzing ne se contente donc pas de
générer des données aléatoires : il conserve et réutilise les entrées qui ouvrent de
nouveaux chemins d’exécution.
Cet exemple est volontairement minimal, mais il illustre bien le principe général : le fuzzer fournit les entrées, le harness les injecte dans une fonction cible, et le sanitizer agit comme oracle pour signaler les comportements incorrects.