
Toolchain Go orientata alla sicurezza, focalizzata su capacità di fuzzing all'avanguardia.
# gosentry
[](https://github.com/trailofbits/gosentry/actions/workflows/go.yml)
gosentry è un fork della toolchain Go incentrato sulla sicurezza, che integra numerose funzionalità per campagne di fuzzing all'avanguardia su codebase Go. Se prima usavi `go test -fuzz`, dovresti usare gosentry come sostituto.
Include vari miglioramenti per il fuzzing e rilevatori di bug che non sono presenti nativamente nella toolchain Go. Vedi TLDR; qui sotto. Puoi anche leggere l'articolo del blog associato [qui](https://blog.trailofbits.com/2026/05/12/go-fuzzing-was-missing-half-the-toolkit.-we-forked-the-toolchain-to-fix-it./).
**TLDR (funzionalità e opzioni)**:
- Esegui il fuzzing direttamente su input di tipo `struct` (nessun parser personalizzato necessario). Aggiungi seed con `f.Add(Input{N: 7, S: "hi"})` e poi `f.Fuzz(func(t *testing.T, in Input) { ... })`.
- Va in panic in caso di overflow di interi e rileva problemi aritmetici
- Fuzza con LibAFL per tecniche di fuzzing all'avanguardia come la risoluzione dei vincoli di percorso
- Genera/muta input a partire da una grammatica per evitare mutazioni inutili. La mutazione genera un'operazione matematica valida, ad esempio `X + Y - Z` può diventare `X / U + Z - 14` invece di `X + Yè - Z`
- Panic su funzioni selezionate (come i logger di errori critici) e crash quando viene chiamata
- Concentra il fuzzer sulle righe modificate di recente E sulla nuova copertura, per mirare principalmente ai nuovi commit
- Rileva le data race in fase di fuzzing
- Rileva i leak di goroutine in fase di fuzzing
- Rileva esecuzioni bloccate con timeout in fase di fuzzing
- Genera un report di copertura HTML dal corpus di una campagna di fuzzing con un'unica CLI
## Sommario
- [Compilazione](#build)
- [Funzionalità 1: Fuzzing consapevole delle struct (fuzz di struct come input)](#feature-1-struct-aware-fuzzing-fuzz-structs-as-inputs)
- [Funzionalità 2: Rilevamento di overflow di interi e problemi di troncamento](#feature-2-integer-overflow-and-truncation-issues-detection)
- [Funzionalità 3: Panic su funzioni selezionate](#feature-3-panic-on-selected-functions)
- [Funzionalità 4: Fuzzing all'avanguardia con LibAFL](#feature-4-libafl-state-of-the-art-fuzzing)
- [Funzionalità 5: Fuzzing orientato al Git blame (sperimentale)](#feature-5-git-blame-oriented-fuzzing-experimental)
- [Funzionalità 6: Rileva race condition, leak di goroutine e blocchi in fase di fuzzing](#feature-6-detect-race-conditions-goroutine-leaks-and-hangs-at-fuzz-time)
- [Funzionalità 7: Fuzzing basato su grammatiche (Nautilus)](#feature-7-grammar-based-fuzzing-nautilus)
- [Funzionalità 8: Genera report di copertura del fuzzing dalla campagna](#feature-8-generate-go-coverage-reports-from-fuzzing-campaign)
- [Trofei](#trophies)
- [Crediti](#credits)
## Compilazione```bash
cd src && ./make.bash # Produces `../bin/go`. See `GOFLAGS` below.
```
> [!TIP]
> Documentazione per contributori: leggi `docs/gosentry/index.md` per una mappa del codice, il ciclo di sviluppo consigliato, i punti di ingresso della CI e gli script di benchmark.
> Questo fork utilizza la Pull GitHub App per aprire e unire automaticamente le PR da `golang/go:master` in `master`, assicurandoci di non rimanere mai indietro rispetto agli ultimi aggiornamenti della toolchain Go.
## Funzionalità 1: Fuzzing consapevole delle struct (fuzz di struct come input)
#### Panoramica
Il fuzzing nativo di Go (`go test -fuzz=...`) supporta solo un piccolo insieme di tipi scalari come parametri di fuzz (`[]byte`, `string`, numeri, ...). In gosentry, puoi anche fare fuzz di **tipi compositi** costruiti da quegli scalari: struct, array, slice e puntatori.
Questo è utile quando il tuo codice accetta naturalmente input strutturati e non vuoi costruire un encoder/decoder personalizzato solo per inizializzare e mutare il corpus.
Vedi `test/gosentry/examples/multiargs` e `test/gosentry/examples/composite` per esempi.
#### Esempio semplice```go
type Input struct {
Data []byte
S string
N int
OK bool
}
func FuzzStructInput(f *testing.F) {
// Seed the initial corpus with a Go struct (gosentry feature).
f.Add(Input{Data: []byte("A"), S: "B", N: 7, OK: true})
f.Fuzz(func(t *testing.T, in Input) {
if in.OK && in.N == 1337 && in.S == "BOOMMOOB" && bytes.Equal(in.Data, []byte("A")) {
t.Fatalf("boom")
}
})
}
```
<details>
<summary><strong>Come funzionano i seed delle struct (<code>f.Add</code>) e il fuzzing delle struct (la colla creata)</strong></summary>
Il fuzzer nativo di Go non può fare fuzzing direttamente su un valore `struct` (sa solo mutare una piccola lista di tipi scalari). gosentry aggiunge un piccolo strato di colla: quando il tuo fuzz target usa tipi compositi (come `Input`), gosentry esegue il fuzzing su un singolo `[]byte` dietro le quinte. A ogni esecuzione, **decodifica** quei byte nella tua struct (campo per campo, ricorsivamente per slice/array/puntatori) e poi chiama la callback `f.Fuzz` con il valore decodificato. La stessa **codifica** viene usata per i seed, quindi `f.Add(Input{...})` diventa una voce di corpus `[]byte` codificata che il fuzzer può riutilizzare e mutare come qualsiasi altro seed.
I fuzzer (incluso LibAFL) mutano byte grezzi, quindi vogliamo un decoder che possa trasformare **qualsiasi** slice di byte in un valore di struct "qualsiasi" e continuare. JSON/`gob` rifiuterebbero la maggior parte degli input casuali (male per la copertura), e inoltre non popolano i campi non esportati, mentre il fuzzing trae spesso beneficio dalla rottura degli invarianti. Questo formato personalizzato è piccolo, veloce, deterministico e tollerante ai dati malformati.
Dietro le quinte, usa il semplice formato binario di gosentry (non `gob`, non JSON). Il codice si trova in `src/testing/libafl.go`:
- Encode: `libaflMarshalInputs` / `libaflAppendValue`
- Decode: `libaflUnmarshalArgs` / `libaflDecodeValue`
Regole di codifica (a livello generale):
- `bool`: 1 byte (`0` o `1`)
- Interi: byte little-endian (`int`/`uint` sono 8 byte)
- Float: bit IEEE-754 in little-endian (`float32` = 4 byte, `float64` = 8 byte)
- `string`: `uvarint(len)` poi byte grezzi della stringa
- `[]byte`: `uvarint(len)` poi byte grezzi
- Altre slice: `uvarint(len)` poi ogni elemento codificato
- Struct: campi codificati nell'ordine di dichiarazione
- Puntatori: 1 byte (`0` = nil, `1` = presente) poi il valore puntato
</details>
## Funzionalità 2: Rilevamento di problemi di overflow e troncamento degli interi
#### Panoramica
Questo lavoro trae ispirazione dal [go-panikint](https://github.com/trailofbits/go-panikint) sviluppato in precedenza. Aggiunge il rilevamento di overflow/underflow per le operazioni aritmetiche sugli interi e (opzionalmente) il rilevamento del troncamento di tipo per le conversioni tra interi. Quando viene rilevato overflow o troncamento, viene generato un panic con un messaggio di errore dettagliato, che include il tipo di operazione specifico e i tipi di interi coinvolti.
_Operazioni aritmetiche_: Gestisce addizione `+`, sottrazione `-`, moltiplicazione `*` e divisione `/` sia per i tipi interi con segno che senza segno. Per gli interi con segno, copre `int8`, `int16`, `int32`. Per gli interi senza segno, copre `uint8`, `uint16`, `uint32`, `uint64`. Il caso della divisione rileva specificamente la condizione di overflow `MIN_INT / -1` per gli interi con segno. `int64` e `uintptr` non sono controllati per le operazioni aritmetiche.
_Rilevamento del troncamento di tipo_: Rileva conversioni tra tipi interi potenzialmente con perdita di dati. Copre tutti i tipi interi: `int8`, `int16`, `int32`, `int64`, `uint8`, `uint16`, `uint32`, `uint64`. Esclude `uintptr` a causa dell'uso dipendente dalla piattaforma. Questa funzione è disabilitata per impostazione predefinita.
Il rilevamento di overflow è abilitato per impostazione predefinita. Per disabilitarlo, aggiungi `GOFLAGS='-gcflags=-overflowdetect=false'` prima del tuo `./make.bash`. Puoi anche abilitare il controllo dei problemi di troncamento con: `-gcflags=-truncationdetect=true`
#### Come funziona
Questa funzionalità modifica la generazione SSA del compilatore in modo che le operazioni aritmetiche sugli interi e le conversioni tra interi ricevano controlli runtime aggiuntivi che chiamano il runtime per generare un panic con un messaggio di errore dettagliato quando viene rilevato un bug. I controlli vengono applicati usando un filtro basato sulla posizione nel codice sorgente, così il codice utente viene strumentato mentre i file della libreria standard e le dipendenze (cache dei moduli e `vendor/`) vengono saltati.
Puoi leggere il post del blog associato [**qui**](https://blog.trailofbits.com/2025/12/31/detect-gos-silent-arithmetic-bugs-with-go-panikint/).
#### Soppressione dei falsi positivi
Aggiungi un marcatore sulla stessa riga dell'operazione o sulla riga immediatamente precedente per sopprimere un report specifico:
- Overflow/underflow: `overflow_false_positive`
- Troncamento: `truncation_false_positive`
Esempio:```go
// overflow_false_positive
intentionalOverflow := a + b
// truncation_false_positive
x := uint8(big)
sum2 := a + b // overflow_false_positive
x2 := uint8(big) // truncation_false_positive
```
A volte questo potrebbe non funzionare, perché Go sta in-lineando la funzione. Se `// overflow_false_positive` non basta, aggiungi `//go:noinline` prima della firma della tua funzione.
## Funzionalità 3: Panico su funzioni selezionate
Quando si eseguono test di fuzzing sui target, potrebbe interessarci innescare un panico quando vengono chiamate determinate funzioni. Ad esempio, alcuni software possono emettere messaggi `log.error` invece di andare in panico, anche se tali condizioni spesso indicano stati che i ricercatori di sicurezza vorrebbero rilevare durante il fuzzing.
Tuttavia, questi errori sono solitamente gestiti internamente (ad esempio tramite meccanismi di retry o pausa, oppure stampando messaggi nei log), il che li rende in gran parte invisibili ai fuzzer. L'obiettivo di questa funzionalità è risolvere questo problema.
#### Come usarlo
Compila gosentry, poi usa il flag `--panic-on`.```bash
./bin/go test -fuzz=FuzzHarness --use-libafl --focus-on-new-code=false --catch-races=false --catch-leaks=false --panic-on="test_go_panicon.(*Logger).Warning,test_go_panicon.(*Logger).Error"
```
L'esempio precedente andrebbe in panic quando viene chiamata `(*Logger).Warning` o `(*Logger).Error` (elenco separato da virgole).
<details>
<summary><strong>Come funziona la funzionalità di panic su funzioni selezionate</strong></summary>```text
┌───────────────────────────────────────────────────────────────────────────┐
│ 1) gosentry `go test` │
│ - parses + validates `-panic-on=...` against packages being built │
│ - forwards patterns to the compiler via `-panic-on-call=...` │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 2) `cmd/compile` │
│ - prevents inlining of matching calls so the call stays visible │
│ - SSA pass inserts a call to `runtime.panicOnCall(...)` │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 3) `runtime.panicOnCall` │
│ - panics with: "panic-on-call: func-name" │
└───────────────────────────────────────────────────────────────────────────┘
```
In pratica, questo fa sì che qualsiasi punto di chiamata corrispondente si comporti come un crash/panic per i fuzzer (nota: solo i punti di chiamata statici possono essere intercettati).
</details>
## Funzionalità 4: fuzzing all'avanguardia con LibAFL
LibAFL offre prestazioni *di gran lunga* migliori rispetto al tradizionale fuzzer Go. Quando si esegue il fuzzing (`go test -fuzz=...`), gosentry utilizza [LibAFL](https://github.com/AFLplusplus/LibAFL) **per impostazione predefinita** (runner in `golibafl/`).
Nota sulla stabilità: in modalità LibAFL, gosentry forza `GODEBUG=updatemaxprocs=0` (disabilita gli aggiornamenti automatici di GOMAXPROCS a runtime) per evitare un crash intermittente della CI su Linux ("sync: inconsistent mutex state"). I dettagli sono in `misc/gosentry/USE_LIBAFL.md`.
Quando si utilizza LibAFL (di default), è necessario scegliere esplicitamente se abilitare la pianificazione git-aware: `--focus-on-new-code=true|false`. Maggiori informazioni in [questo file Markdown.](https://github.com/trailofbits/gosentry/blob/master/misc/gosentry/USE_LIBAFL.md)
Puoi anche passare un file di configurazione JSONC opzionale per LibAFL (incluse le opzioni di fuzzing grammaticale), vedi [qui.](https://github.com/trailofbits/gosentry/blob/master/misc/gosentry/libafl.config.jsonc)
Con `"stop_all_fuzzers_on_panic": false`, LibAFL salva ogni crash e riavvia il proprio client per continuare il fuzzing.```bash
./bin/go test -fuzz=FuzzHarness --focus-on-new-code=false --catch-races=false --catch-leaks=false --libafl-config=path/to/libafl.jsonc # optional --libafl-config
```
Use `-fuzztime=1m` per fermare una campagna LibAFL dopo un minuto.
La generazione del report di copertura dal corpus di una campagna LibAFL è documentata nella [Funzione 8](#feature-8-generate-go-coverage-reports-from-fuzzing-campaign).
Il fuzzing basato su grammatiche (Nautilus) è documentato nella [Funzione 7](#feature-7-grammar-based-fuzzing-nautilus).
<details>
<summary><strong>Come Go + LibAFL sono collegati tra loro</strong></summary>```text
┌───────────────────────────────────────────────────────────────────────────┐
│ 1) gosentry `go test` │
│ - captures `testing.F.Fuzz(...)` callback │
│ - generates extra source file: `_libaflmain.go` │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 2) Generated bridge: `_libaflmain.go` │
│ - provides libFuzzer-style C ABI entrypoints: │
│ LLVMFuzzerInitialize │
│ LLVMFuzzerTestOneInput │
│ - adapts bytes -> Go types -> calls the captured fuzz callback │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 3) `libharness.a` (static archive on disk) contains: │
│ - compiled objects for all test package (+ dependencies) │
│ - generated `_testmain.go` + `_libaflmain.go` │
│ - LLVMFuzzerInitialize │
│ - LLVMFuzzerTestOneInput │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 4) `golibafl/` (Rust + LibAFL) │
│ env: HARNESS_LIB=/path/to/libharness.a │
│ fuzz loop: mutate input -> LLVMFuzzerTestOneInput(data) -> observe │
└───────────────────────────────────────────────────────────────────────────┘
```
In `--use-libafl` mode, gosentry compila `libharness.a` e il runner Rust `golibafl` lo esegue in-process tramite gli entrypoint di libFuzzer. Nota: `HARNESS_LIB` può puntare a qualsiasi nome di archivio dell'harness (ad esempio `libharness_race.a` usato da `--catch-races`).
</details>
<details>
<summary><strong>Come funziona la strumentazione della copertura (LibAFL + target Go)</strong></summary>
In modalità `--use-libafl`, gosentry compila l'harness Go con la strumentazione della copertura abilitata. Questa aggiunge piccoli contatori al codice che cambiano quando diverse parti del tuo programma vengono eseguite. Quando l'harness viene avviato all'interno di `golibafl`, il runtime Go espone questi contatori a LibAFL. LibAFL li legge dopo ogni input per vedere quale codice è stato eseguito e usa quella copertura per guidare le mutazioni successive.
</details>
##### Limitazioni
Parliamo della motivazione dietro l'uso di LibAFL. Il fuzzing con `go test -fuzz` è _molto_ indietro rispetto alle tecniche di fuzzing all'avanguardia. Un buon esempio è CMPLOG/Redqueen di AFL++. Queste funzionalità consentono ai fuzzer di risolvere determinati vincoli. Supponiamo il seguente frammento di codice```go
if input == "IMARANDOMSTRINGJUSTCMPLOGMEMAN" {
panic("this string is illegal")
}
```
SOTA fuzzers come AFL++ o LibAFL troverebbero il panic istantaneamente in quel caso. Tuttavia, il fuzzer nativo di Go non lo troverebbe. Questo è un divario enorme che limita **molto** l'esplorazione della copertura.
I benchmark qui sotto mostrano questi limiti. Nota che questi benchmark possono essere **riprodotti** e migliorati tramite il [repository gosentry-bench-libafl](https://github.com/kevin-valerio/gosentry-bench-libafl/tree/main).
##### Benchmark 1:
Il grafico seguente mostra l'evoluzione del numero di righe coperte durante il fuzzing di [UUID](https://github.com/google/uuid) di Google con LibAFL rispetto al fuzzer nativo di Go.

##### Benchmark 2:
Il grafico seguente mostra l'evoluzione del numero di righe coperte durante il fuzzing di [go-ethereum](https://github.com/ethereum/go-ethereum) con LibAFL rispetto al fuzzer nativo di Go.

#### Esempio
Puoi testarlo su alcuni harness di fuzzing in `test/gosentry/examples/`.```bash
cd test/gosentry/examples/reverse
../../../../bin/go test -fuzz=FuzzReverse --focus-on-new-code=false --catch-races=false --catch-leaks=false
```
Ferma la campagna di fuzzing con Ctrl+C.
##### Directory di output di LibAFL (identità della campagna / riutilizzo del corpus)
gosentry memorizza lo stato della campagna di LibAFL (corpus, crash, ecc.) sotto la root della cache di fuzzing di Go (circa `$(go env GOCACHE)/fuzz`), in una directory deterministica derivata dallo **stesso pacchetto + stesso target di fuzzing** (e dalla stessa root del progetto).
Questo significa che fermando (Ctrl+C) e riavviando la stessa campagna di fuzzing, per impostazione predefinita si continuerà dal precedente corpus `queue/` di LibAFL.
Il percorso viene stampato al termine dell'esecuzione:```text
libafl output dir: /full/path/to/.../fuzz/<pkg import path>/libafl/<project>/<harness>
```
Note:
- `<harness>` è il nome del target di fuzzing quando `-fuzz` è un semplice identificatore come `FuzzXxx` (o `^FuzzXxx$`), altrimenti è `pattern-<hash>`.
- La generazione della coverage (`--generate-coverage`) usa la stessa regola per trovare il corpus `queue/` giusto, quindi deve essere eseguita dallo stesso package con lo stesso `-fuzz=...`.
## Funzionalità 5: fuzzing orientato a Git blame (sperimentale)
#### Panoramica
Il fuzzing guidato dalla coverage è ottimo per esplorare nuovi percorsi, ma tratta tutto il codice coperto come ugualmente interessante. Quando si esegue il fuzzing su codebase di grandi dimensioni, potrebbe essere utile orientare il fuzzer verso il codice modificato di recente, dove è più probabile che vengano introdotte regressioni e bug. In modalità LibAFL, gosentry può usare `git blame` per preferire input che eseguono righe modificate di recente (mantenendo comunque la guida della coverage come segnale primario).
Questo lavoro si basa su un precedente lavoro di [LibAFL-git-aware](https://github.com/kevin-valerio/LibAFL-git-aware). Tutti i dettagli tecnici approfonditi sono documentati lì.
#### Come usarlo
Abilita la schedulazione git-aware con `--focus-on-new-code=true`:```bash
./bin/go test -fuzz=FuzzHarness --use-libafl --focus-on-new-code=true --catch-races=false --catch-leaks=false
```
Questa modalità richiede `git` (per eseguire `git blame`) e `go tool addr2line` per mappare i contatori di copertura alla sorgente `file:line`.
<details>
<summary><strong>Come funziona il fuzzing basato su git blame</strong></summary>```text
┌───────────────────────────────────────────────────────────────────────────┐
│ 1) gosentry `go test -fuzz` │
│ - builds `libharness.a` (contains `go.o` + `.go.fuzzcntrs`) │
│ - runs `golibafl` with `GOLIBAFL_FOCUS_ON_NEW_CODE=1` │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 2) `golibafl` generates a cached "git recency map" │
│ - maps coverage counters -> (file:line) via `go tool addr2line` │
│ - runs `git blame` to get a timestamp per line │
│ - stores timestamps in `git_recency_map.bin` │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 3) LibAFL scheduler uses the recency map │
│ - coverage decides what enters the corpus │
│ - among the corpus, prioritize inputs that hit newer lines │
└───────────────────────────────────────────────────────────────────────────┘
```
</details>
<details>
<summary><strong>Come gosentry costruisce <code>git_recency_map.bin</code></strong></summary>
`.go.fuzzcntrs` è la sezione del linker che contiene i **contatori di copertura a 8 bit** in stile libFuzzer di Go (abilitati con `-gcflags=all=-d=libfuzzer`); ogni byte indica "quante volte è stato raggiunto questo punto instrumentato". Quando `--focus-on-new-code=true`, `golibafl` genera `git_recency_map.bin`:
1. Estrarre `go.o` da `libharness.a`.
2. Leggere la dimensione della sezione `.go.fuzzcntrs` per ottenere il numero di contatori `N`.
3. Analizzare le rilocazioni di `.text` che referenziano i simboli di `.go.fuzzcntrs` per recuperare l'indirizzo di ciascun indice di contatore.
4. Risolvere ogni indirizzo in `file:line` usando `go tool addr2line`.
5. Eseguire `git blame --line-porcelain` per ottenere il `committer-time` per ogni riga.
6. Scrivere `git_recency_map.bin` come `u64 head_time` + `u64 N` + `N * u64 timestamps` (little-endian). Le voci non mappate usano timestamp `0`.
</details>
<details>
<summary><strong>Benchmark 1 (go-ethereum / geth): baseline vs git-aware</strong></summary>
Eseguito con `misc/gosentry/bench_focus_on_new_code_geth.sh --trials 5 --warmup 600 --timeout 200`.```text
gitaware_5: crash (7122ms)
baseline results:
trial 1: crash (107747ms)
trial 2: crash (146415ms)
trial 3: crash (37902ms)
trial 4: crash (154034ms)
trial 5: timeout (200000ms)
baseline crashes: 4/5 (timeouts=1, errors=0)
baseline median (capped to timeout): 146.415s
git-aware results:
trial 1: timeout (200000ms)
trial 2: crash (87432ms)
trial 3: crash (61733ms)
trial 4: crash (157540ms)
trial 5: crash (7122ms)
git-aware crashes: 4/5 (timeouts=1, errors=0)
git-aware median (capped to timeout): 87.432s
```
</details>
## Funzionalità 6: Rilevare race condition, goroutine leak e blocchi (timeout) durante il fuzzing
##### Rilevamento di blocchi confermati (timeout di LibAFL)
Quando si esegue il fuzzing con LibAFL, un'esecuzione dell'harness può andare in **timeout** (ad esempio a causa di un deadlock / goroutine bloccate in attesa, o di un percorso estremamente lento).
Per ridurre i falsi positivi, gosentry tratta un timeout come un candidato blocco e lo conferma riproducendo l'input andato in timeout alcune volte con un timeout più ampio. In caso di blocco confermato, gosentry scrive l'input in `<libafl output dir>/hangs/` e interrompe la campagna di fuzzing (lo tratta come un bug/crash).
Prima di uscire, `golibafl` tenta di minimizzare l'input che causa il crash/blocco (best-effort; i blocchi sono limitati a ~60s in totale).
Nota: la conferma dei blocchi viene eseguita anche durante l'importazione/generazione iniziale del corpus, quindi i target che vanno in timeout su ogni input possono comunque essere rilevati in modo deterministico.
Questa funzionalità è configurata tramite `--libafl-config`:
- `catch_hangs` (predefinito: `true`)
- `hang_timeout_ms` (predefinito: `10000`)
- `hang_confirm_runs` (predefinito: `3`)
##### Rilevamento di data race (`--catch-races`)
gosentry può eseguire un ciclo di replay `-race` separato che osserva la directory `queue/` di LibAFL e riproduce i seed appena scoperti con `GORACE=halt_on_error=1`.
Il ciclo di replay compila un archivio separato dell'harness `-race` solo per il replay (senza strumentazione di copertura per il fuzzing).
Quando viene rilevata una data race durante il replay, gosentry stampa il report completo del race detector prima del riepilogo `catch-races:` e del comando di riproduzione.
Nota: il race detector di Go rileva solo data race **all'interno di una singola esecuzione dell'harness** (race tra goroutine nello stesso processo che accedono alla stessa memoria senza una corretta sincronizzazione). `--catch-races` non rileverà le race se il seed non innesca la concorrenza problematica e non rileva race tra processi diversi.
<details>
<summary><strong>Come funziona la modalità data race</strong></summary>
Questa modalità avvia un piccolo monitor all'interno di `go test` (stesso processo padre) e resta attivo per l'intera campagna di fuzzing.
- Quando: prima che venga avviato il processo principale di fuzzing LibAFL, gosentry compila l'harness di replay + il runner.
- Monitoraggio: prima che il fuzzing inizi, gosentry acquisisce un'istantanea del contenuto iniziale di `<libafl output dir>/queue/` in un set `seen`. Una goroutine interroga quindi `<libafl output dir>/queue/` ogni ~1s e riproduce solo i seed appena creati (ignora i file nascosti e i `*.metadata`).```text
Legend: output/... = <libafl output dir>/...
┌───────────────────────────────────────────────────────────────────────────┐
│ 1) Main LibAFL fuzzing run │
│ - `golibafl` writes new seeds to `output/queue/` │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 2) `--catch-races` sidecar setup │
│ - builds replay harness: `libharness_race.a` (`go test -race ...`) │
│ - builds replay runner: `golibafl-race` (linked against race harness) │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 3) Replay loop │
│ - polls `output/queue/` for new seeds │
│ - runs: `GORACE=halt_on_error=1 golibafl-race run --input <seed>` │
│ (2 workers × 3 repeats per seed) │
└───────────────┬───────────────────────────────────────────────────────────┘
v
┌───────────────────────────────────────────────────────────────────────────┐
│ 4) On "DATA RACE" │
│ - prints the race detector report │
│ - copies seed to `output/races/` │
```
---
[Read more](https://github.com/trailofbits/gosentry)